os/timer: interpolate time via timestamps

Previously, the Genode::Timer::curr_time always used the
Timer_session::elapsed_ms RPC as back end.  Now, Genode::Timer reads
this remote time only in a periodic fashion independently from the calls
to Genode::Timer::curr_time. If now one calls Genode::Timer::curr_time,
the function takes the last read remote time value and adapts it using
the timestamp difference since the remote-time read. The conversion
factor from timestamps to time is estimated on every remote-time read
using the last read remote-time value and the timestamp difference since
the last remote time read.

This commit also re-works the timeout test. The test now has two stages.
In the first stage, it tests fast polling of the
Genode::Timer::curr_time. This stage checks the error between locally
interpolated and timer-driver time as well as wether the locally
interpolated time is monotone and sufficiently homogeneous. In the
second stage several periodic and one-shot timeouts are scheduled at
once. This stage checks if the timeouts trigger sufficiently precise.

This commit adds the new Kernel::time syscall to base-hw. The syscall is
solely used by the Genode::Timer on base-hw as substitute for the
timestamp. This is because on ARM, the timestamp function uses the ARM
performance counter that stops counting when the WFI (wait for
interrupt) instruction is active. This instruction, however is used by
the base-hw idle contexts that get active when no user thread needs to
be scheduled.  Thus, the ARM performance counter is not a good choice for
time interpolation and we use the kernel internal time instead.

With this commit, the timeout library becomes a basic library. That means
that it is linked against the LDSO which then provides it to the program it
serves. Furthermore, you can't use the timeout library anymore without the
LDSO because through the kernel-dependent LDSO make-files we can achieve a
kernel-dependent timeout implementation.

This commit introduces a structured Duration type that shall successively
replace the use of Microseconds, Milliseconds, and integer types for duration
values.

Open issues:

* The timeout test fails on Raspberry PI because of precision errors in the
  first stage. However, this does not render the framework unusable in general
  on the RPI but merely is an issue when speaking of microseconds precision.

* If we run on ARM with another Kernel than HW the timestamp speed may
  continuously vary from almost 0 up to CPU speed. The Timer, however,
  only uses interpolation if the timestamp speed remained stable (12.5%
  tolerance) for at least 3 observation periods. Currently, one period is
  100ms, so its 300ms. As long as this is not the case,
  Timer_session::elapsed_ms is called instead.

  Anyway, it might happen that the CPU load was stable for some time so
  interpolation becomes active and now the timestamp speed drops. In the
  worst case, we would now have 100ms of slowed down time. The bad thing
  about it would be, that this also affects the timeout of the period.
  Thus, it might "freeze" the local time for more than 100ms.

  On the other hand, if the timestamp speed suddenly raises after some
  stable time, interpolated time can get too fast. This would shorten the
  period but nonetheless may result in drifting away into the far future.
  Now we would have the problem that we can't deliver the real time
  anymore until it has caught up because the output of Timer::curr_time
  shall be monotone. So, effectively local time might "freeze" again for
  more than 100ms.

  It would be a solution to not use the Trace::timestamp on ARM w/o HW but
  a function whose return value causes the Timer to never use
  interpolation because of its stability policy.

Fixes #2400
This commit is contained in:
Martin Stein 2017-04-22 00:52:23 +02:00 committed by Christian Helmuth
parent dc566b7d52
commit c70fed29f7
74 changed files with 1640 additions and 536 deletions

View File

@ -1,6 +1,6 @@
include $(BASE_DIR)/lib/mk/base.inc
LIBS += syscall-fiasco
LIBS += syscall-fiasco timeout
SRC_CC += thread_start.cc
SRC_CC += cache.cc

View File

@ -1,6 +1,6 @@
include $(BASE_DIR)/lib/mk/base.inc
LIBS += base-foc-common syscall-foc cxx
LIBS += base-foc-common syscall-foc cxx timeout
SRC_CC += cap_map_remove.cc cap_alloc.cc
SRC_CC += thread_start.cc

View File

@ -42,6 +42,7 @@ namespace Kernel
constexpr Call_arg call_id_timeout() { return 16; }
constexpr Call_arg call_id_timeout_age_us() { return 17; }
constexpr Call_arg call_id_timeout_max_us() { return 18; }
constexpr Call_arg call_id_time() { return 19; }
/*****************************************************************
@ -106,6 +107,18 @@ namespace Kernel
}
/**
* Return value of a free-running, uniform counter
*
* The counter has a constant frequency and does not wrap twice during
* a time period of 'timeout_max_us()' microseconds.
*/
inline time_t time()
{
return call(call_id_time());
}
/**
* Return the constant maximum installable timeout in microseconds
*

View File

@ -6,4 +6,4 @@ SRC_CC += capability.cc
SRC_CC += cache.cc
SRC_CC += raw_write_string.cc
LIBS += startup-hw base-hw-common
LIBS += startup-hw base-hw-common timeout-hw

View File

@ -0,0 +1,10 @@
SRC_CC += timeout.cc
SRC_CC += timer_connection.cc
SRC_CC += hw/timer_connection_timestamp.cc
SRC_CC += duration.cc
LIBS += alarm
INC_DIR += $(BASE_DIR)/src/include
vpath % $(BASE_DIR)/../os/src/lib/timeout

View File

@ -42,6 +42,9 @@ time_t Cpu_job::timeout_age_us(Timeout const * const timeout) const
}
time_t Cpu_job::time() const { return _cpu->time(); }
time_t Cpu_job::timeout_max_us() const
{
return _cpu->timeout_max_us();

View File

@ -196,6 +196,8 @@ class Kernel::Cpu_job : public Genode::Cpu::User_context, public Cpu_share
time_t timeout_max_us() const;
time_t time() const;
/***************
** Accessors **
***************/
@ -323,6 +325,8 @@ class Kernel::Cpu : public Genode::Cpu, public Irq::Pool, private Timeout
time_t timeout_max_us() const;
time_t time() const { return _timer.time(); }
/***************
** Accessors **
***************/

View File

@ -564,6 +564,7 @@ void Thread::_call()
case call_id_timeout(): _call_timeout(); return;
case call_id_timeout_age_us(): _call_timeout_age_us(); return;
case call_id_timeout_max_us(): _call_timeout_max_us(); return;
case call_id_time(): user_arg_0(Cpu_job::time()); return;
default:
/* check wether this is a core thread */
if (!_core()) {

View File

@ -99,6 +99,8 @@ class Kernel::Timer
unsigned interrupt_id() const;
static void init_cpu_local();
time_t time() const { return _time; }
};
#endif /* _CORE__KERNEL__TIMER_H_ */

View File

@ -6,7 +6,7 @@
include $(REP_DIR)/lib/mk/base-linux.inc
LIBS += startup-linux base-linux-common
LIBS += startup-linux base-linux-common timeout
SRC_CC += thread.cc thread_myself.cc thread_linux.cc
SRC_CC += capability_space.cc capability_raw.cc
SRC_CC += attach_stack_area.cc

View File

@ -4,7 +4,7 @@ vpath new_delete.cc $(BASE_DIR)/src/lib/cxx
vpath lx_hybrid.cc $(REP_DIR)/src/lib/lx_hybrid
# add parts of the base library that are shared with core
LIBS += base-linux-common
LIBS += base-linux-common timeout
# non-core parts of the base library (except for the startup code)
include $(REP_DIR)/lib/mk/base-linux.inc

View File

@ -1,5 +1,5 @@
include $(BASE_DIR)/lib/mk/base.inc
LIBS += base-nova-common cxx
LIBS += base-nova-common cxx timeout
SRC_CC += thread_start.cc
SRC_CC += cache.cc

View File

@ -1,6 +1,6 @@
include $(BASE_DIR)/lib/mk/base.inc
LIBS += base-okl4-common syscall-okl4
LIBS += base-okl4-common syscall-okl4 timeout
SRC_CC += thread_start.cc
SRC_CC += cache.cc
SRC_CC += capability_space.cc

View File

@ -1,6 +1,6 @@
include $(BASE_DIR)/lib/mk/base.inc
LIBS += base-pistachio-common syscall-pistachio cxx
LIBS += base-pistachio-common syscall-pistachio cxx timeout
SRC_CC += thread_start.cc
SRC_CC += cache.cc

View File

@ -4,4 +4,4 @@ SRC_CC += capability_space.cc
SRC_CC += thread_start.cc thread_init.cc
SRC_CC += cache.cc
LIBS += syscall-sel4 base-sel4-common
LIBS += syscall-sel4 base-sel4-common timeout

View File

@ -46,8 +46,18 @@ _Z13genode_atexitPFvvE T
_Z16main_thread_utcbv T
_Z21genode___cxa_finalizePv T
_Z22__ldso_raise_exceptionv T
_ZN5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T
_ZN5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T
_ZN5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T
_ZN5Timer10Connection8_discardERN6Genode7TimeoutE T
_ZN5Timer10Connection9curr_timeEv T
_ZN5Timer10ConnectionC1ERN6Genode3EnvEPKc T
_ZN5Timer10ConnectionC1Ev T
_ZN5Timer10ConnectionC2ERN6Genode3EnvEPKc T
_ZN5Timer10ConnectionC2Ev T
_ZN6Genode10Entrypoint16_dispatch_signalERNS_6SignalE T
_ZN6Genode10Entrypoint16schedule_suspendEPFvvES2_ T
_ZN6Genode10Entrypoint22Signal_proxy_component6signalEv T
_ZN6Genode10Entrypoint25_process_incoming_signalsEv T
_ZN6Genode10Entrypoint31wait_and_dispatch_one_io_signalEv T
_ZN6Genode10Entrypoint6manageERNS_22Signal_dispatcher_baseE T
@ -56,7 +66,6 @@ _ZN6Genode10EntrypointC1ERNS_3EnvE T
_ZN6Genode10EntrypointC1ERNS_3EnvEmPKc T
_ZN6Genode10EntrypointC2ERNS_3EnvE T
_ZN6Genode10EntrypointC2ERNS_3EnvEmPKc T
_ZN6Genode10Entrypoint22Signal_proxy_component6signalEv T
_ZN6Genode10Ipc_serverC1Ev T
_ZN6Genode10Ipc_serverC2Ev T
_ZN6Genode10Ipc_serverD1Ev T
@ -116,6 +125,7 @@ _ZN6Genode14Signal_contextD2Ev T
_ZN6Genode14Timeout_thread11alarm_timerEv T
_ZN6Genode14Timeout_thread5entryEv T
_ZN6Genode14cache_coherentEmm T
_ZN6Genode14env_deprecatedEv T
_ZN6Genode14ipc_reply_waitERKNS_17Native_capabilityENS_18Rpc_exception_codeERNS_11Msgbuf_baseES5_ T
_ZN6Genode15Alarm_scheduler12_setup_alarmERNS_5AlarmEmm T
_ZN6Genode15Alarm_scheduler13next_deadlineEPm T
@ -186,6 +196,12 @@ _ZN6Genode18Signal_transmitterC1ENS_10CapabilityINS_14Signal_contextEEE T
_ZN6Genode18Signal_transmitterC2ENS_10CapabilityINS_14Signal_contextEEE T
_ZN6Genode18server_socket_pairEv T
_ZN6Genode20env_session_id_spaceEv T
_ZN6Genode23Alarm_timeout_scheduler14handle_timeoutENS_8DurationE T
_ZN6Genode23Alarm_timeout_scheduler18_schedule_one_shotERNS_7TimeoutENS_12MicrosecondsE T
_ZN6Genode23Alarm_timeout_scheduler18_schedule_periodicERNS_7TimeoutENS_12MicrosecondsE T
_ZN6Genode23Alarm_timeout_scheduler7_enableEv T
_ZN6Genode23Alarm_timeout_schedulerC1ERNS_11Time_sourceE T
_ZN6Genode23Alarm_timeout_schedulerC2ERNS_11Time_sourceE T
_ZN6Genode25env_stack_area_region_mapE B 4
_ZN6Genode26env_stack_area_ram_sessionE B 4
_ZN6Genode29upgrade_pd_quota_non_blockingENS_9Ram_quotaENS_9Cap_quotaE T
@ -195,7 +211,6 @@ _ZN6Genode3Log8_releaseEv T
_ZN6Genode3Raw7_outputEv T
_ZN6Genode3Raw8_acquireEv T
_ZN6Genode3Raw8_releaseEv T
_ZN6Genode14env_deprecatedEv T
_ZN6Genode4Heap11quota_limitEm T
_ZN6Genode4Heap4freeEPvm T
_ZN6Genode4Heap5allocEmPPv T
@ -307,10 +322,18 @@ _ZN6Genode7Console7vprintfEPKcP13__va_list_tag T
_ZN6Genode7Console7vprintfEPKcPc T
_ZN6Genode7Console7vprintfEPKcPv T
_ZN6Genode7Console7vprintfEPKcSt9__va_list T
_ZN6Genode7Timeout17schedule_one_shotENS_12MicrosecondsERNS0_7HandlerE T
_ZN6Genode7Timeout17schedule_periodicENS_12MicrosecondsERNS0_7HandlerE T
_ZN6Genode7Timeout5AlarmD0Ev W
_ZN6Genode7Timeout5AlarmD1Ev W
_ZN6Genode7Timeout5AlarmD2Ev W
_ZN6Genode7Timeout7discardEv T
_ZN6Genode7cap_mapEv T
_ZN6Genode7vprintfEPKcP13__va_list_tag T
_ZN6Genode7vprintfEPKcPc T
_ZN6Genode7vprintfEPKcSt9__va_list T
_ZN6Genode8DurationpLENS_12MicrosecondsE T
_ZN6Genode8DurationpLENS_12MillisecondsE T
_ZN6Genode8ipc_callENS_17Native_capabilityERNS_11Msgbuf_baseES2_m T
_ZN6Genode9ipc_replyENS_17Native_capabilityENS_18Rpc_exception_codeERNS_11Msgbuf_baseE T
_ZNK6Genode11Sliced_heap8overheadEm T
@ -335,6 +358,7 @@ _ZNK6Genode5Child21notify_resource_availEv T
_ZNK6Genode6Thread10stack_baseEv T
_ZNK6Genode6Thread4nameEv T
_ZNK6Genode6Thread9stack_topEv T
_ZNK6Genode8Duration17trunc_to_plain_usEv T
_ZNKSt13bad_exception4whatEv T
_ZNKSt9exception4whatEv T
_ZNSt13bad_exceptionD0Ev T
@ -361,6 +385,7 @@ _ZTIN10__cxxabiv120__function_type_infoE D 12
_ZTIN10__cxxabiv120__si_class_type_infoE D 12
_ZTIN10__cxxabiv121__vmi_class_type_infoE D 12
_ZTIN10__cxxabiv123__fundamental_type_infoE D 12
_ZTIN5Timer10ConnectionE D 48
_ZTIN6Genode11Sliced_heapE D 12
_ZTIN6Genode14Rpc_entrypointE D 2
_ZTIN6Genode14Rpc_entrypointE D 32
@ -370,6 +395,7 @@ _ZTIN6Genode14Timeout_threadE D 32
_ZTIN6Genode17Region_map_clientE D 12
_ZTIN6Genode17Rm_session_clientE D 12
_ZTIN6Genode18Allocator_avl_baseE D 12
_ZTIN6Genode23Alarm_timeout_schedulerE D 80
_ZTIN6Genode4HeapE D 12
_ZTIN6Genode4SlabE D 12
_ZTIN6Genode5AlarmE D 8
@ -379,6 +405,7 @@ _ZTIN6Genode5ChildE D 40
_ZTIN6Genode6OutputE D 8
_ZTIN6Genode6ThreadE D 8
_ZTIN6Genode7ConsoleE D 8
_ZTIN6Genode7Timeout5AlarmE D 24
_ZTIPDd D 16
_ZTIPDe D 16
_ZTIPDf D 16
@ -484,6 +511,7 @@ _ZTSN10__cxxabiv120__function_type_infoE R
_ZTSN10__cxxabiv120__si_class_type_infoE R
_ZTSN10__cxxabiv121__vmi_class_type_infoE R
_ZTSN10__cxxabiv123__fundamental_type_infoE R
_ZTSN5Timer10ConnectionE R
_ZTSN6Genode11Sliced_heapE R
_ZTSN6Genode14Rpc_entrypointE R
_ZTSN6Genode14Signal_contextE R
@ -491,6 +519,7 @@ _ZTSN6Genode14Timeout_threadE R
_ZTSN6Genode17Region_map_clientE R
_ZTSN6Genode17Rm_session_clientE R
_ZTSN6Genode18Allocator_avl_baseE R
_ZTSN6Genode23Alarm_timeout_schedulerE R
_ZTSN6Genode4HeapE R
_ZTSN6Genode4SlabE R
_ZTSN6Genode5AlarmE R
@ -499,6 +528,7 @@ _ZTSN6Genode5ChildE R
_ZTSN6Genode6OutputE R
_ZTSN6Genode6ThreadE R
_ZTSN6Genode7ConsoleE R
_ZTSN6Genode7Timeout5AlarmE R
_ZTSSt10bad_typeid R
_ZTSSt13bad_exception R
_ZTSSt16bad_array_length R
@ -527,6 +557,7 @@ _ZTVN10__cxxabiv121__vmi_class_type_infoE D 2
_ZTVN10__cxxabiv121__vmi_class_type_infoE D 44
_ZTVN10__cxxabiv123__fundamental_type_infoE D 2
_ZTVN10__cxxabiv123__fundamental_type_infoE D 32
_ZTVN5Timer10ConnectionE D 116
_ZTVN6Genode11Sliced_heapE D 2
_ZTVN6Genode11Sliced_heapE D 36
_ZTVN6Genode14Rpc_entrypointE D 1
@ -541,6 +572,7 @@ _ZTVN6Genode17Rm_session_clientE D 1
_ZTVN6Genode17Rm_session_clientE D 24
_ZTVN6Genode18Allocator_avl_baseE D 4
_ZTVN6Genode18Allocator_avl_baseE D 64
_ZTVN6Genode23Alarm_timeout_schedulerE D 80
_ZTVN6Genode4HeapE D 2
_ZTVN6Genode4HeapE D 36
_ZTVN6Genode4SlabE D 2
@ -557,6 +589,7 @@ _ZTVN6Genode6ThreadE D 1
_ZTVN6Genode6ThreadE D 24
_ZTVN6Genode7ConsoleE D 1
_ZTVN6Genode7ConsoleE D 24
_ZTVN6Genode7Timeout5AlarmE D 40
_ZTVSt10bad_typeid D 1
_ZTVSt10bad_typeid D 20
_ZTVSt13bad_exception D 1
@ -573,6 +606,20 @@ _ZTVSt9exception D 1
_ZTVSt9exception D 20
_ZTVSt9type_info D 2
_ZTVSt9type_info D 32
_ZThn232_N5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T
_ZThn232_N5Timer10Connection9curr_timeEv T
_ZThn236_N5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T
_ZThn236_N5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T
_ZThn236_N5Timer10Connection8_discardERN6Genode7TimeoutE T
_ZThn236_N5Timer10Connection9curr_timeEv T
_ZThn280_N5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T
_ZThn280_N5Timer10Connection9curr_timeEv T
_ZThn288_N5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T
_ZThn288_N5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T
_ZThn288_N5Timer10Connection8_discardERN6Genode7TimeoutE T
_ZThn288_N5Timer10Connection9curr_timeEv T
_ZThn4_N6Genode23Alarm_timeout_scheduler14handle_timeoutENS_8DurationE T
_ZThn8_N6Genode23Alarm_timeout_scheduler14handle_timeoutENS_8DurationE T
_ZZN6Genode18Allocator_avl_base5BlockC4EmmbE10num_blocks V
_ZdlPv W
_ZdlPvPN6Genode11DeallocatorE T

View File

@ -344,7 +344,7 @@ static Genode::Signal_context_capability tick_sig_cap;
void Lx::event_init(Genode::Env &env, Genode::Entrypoint &ep, void (*ticker)())
{
static Timeout handler(env, ep, ticker);
static ::Timeout handler(env, ep, ticker);
_timeout = &handler;
}

View File

@ -24,7 +24,6 @@
#include <input/event.h>
#include <util/color.h>
#include <os/pixel_rgb565.h>
#include <os/timer.h>
/* terminal includes */
#include <terminal/decoder.h>
@ -531,8 +530,7 @@ struct Terminal::Main
Framebuffer::Connection _framebuffer { _env, Framebuffer::Mode() };
Input::Connection _input { _env };
Timer::Connection _timer_conection { _env };
Genode::Timer _timer { _timer_conection, _env.ep() };
Timer::Connection _timer { _env };
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
@ -556,12 +554,12 @@ struct Terminal::Main
Signal_handler<Main> _input_handler {
_env.ep(), *this, &Main::_handle_input };
void _handle_key_repeat(Time_source::Microseconds);
void _handle_key_repeat(Duration);
One_shot_timeout<Main> _key_repeat_timeout {
Timer::One_shot_timeout<Main> _key_repeat_timeout {
_timer, *this, &Main::_handle_key_repeat };
void _handle_flush(Time_source::Microseconds);
void _handle_flush(Duration);
/*
* Time in milliseconds between a change of the terminal content and the
@ -575,7 +573,7 @@ struct Terminal::Main
void _trigger_flush()
{
if (!_flush_scheduled) {
_flush_timeout.start(Time_source::Microseconds{1000*_flush_delay});
_flush_timeout.schedule(Microseconds{1000*_flush_delay});
_flush_scheduled = true;
}
}
@ -590,7 +588,7 @@ struct Terminal::Main
Trigger_flush(Main &main) : _main(main) { }
} _trigger_flush_callback { *this };
One_shot_timeout<Main> _flush_timeout {
Timer::One_shot_timeout<Main> _flush_timeout {
_timer, *this, &Main::_handle_flush };
Main(Genode::Env &env,
@ -657,11 +655,11 @@ void Terminal::Main::_handle_input()
});
if (_repeat_next)
_key_repeat_timeout.start(Time_source::Microseconds{1000*_repeat_next});
_key_repeat_timeout.schedule(Microseconds{1000*_repeat_next});
}
void Terminal::Main::_handle_key_repeat(Time_source::Microseconds)
void Terminal::Main::_handle_key_repeat(Duration)
{
if (_repeat_next) {
@ -675,7 +673,7 @@ void Terminal::Main::_handle_key_repeat(Time_source::Microseconds)
}
void Terminal::Main::_handle_flush(Time_source::Microseconds)
void Terminal::Main::_handle_flush(Duration)
{
_flush_scheduled = false;
_flush_callback_registry.flush();

View File

@ -1,4 +1,4 @@
TARGET = terminal
SRC_CC = main.cc
LIBS = base timeout
LIBS = base
SRC_BIN = $(notdir $(wildcard $(PRG_DIR)/*.tff))

View File

@ -4,7 +4,7 @@
LIBS = libc-string libc-locale libc-stdlib libc-stdio libc-gen libc-gdtoa \
libc-inet libc-stdtime libc-regex libc-compat libc-setjmp libc-mem
LIBS += base vfs timeout
LIBS += base vfs
#
# Back end

View File

@ -16,8 +16,6 @@
#include "task.h"
using namespace Genode;
extern "C" __attribute__((weak))
int _nanosleep(const struct timespec *req, struct timespec *rem)
{

View File

@ -23,7 +23,6 @@
#include <vfs/file_system_factory.h>
#include <vfs/dir_file_system.h>
#include <timer_session/connection.h>
#include <os/timer.h>
/* libc includes */
#include <libc/component.h>
@ -49,7 +48,7 @@ namespace Libc {
class Timeout_handler;
class Io_response_handler;
using Microseconds = Genode::Time_source::Microseconds;
using Genode::Microseconds;
}
@ -167,18 +166,13 @@ class Libc::Env_implementation : public Libc::Env
struct Libc::Timer
{
::Timer::Connection _timer_connection;
Genode::Timer _timer;
::Timer::Connection _timer;
Timer(Genode::Env &env)
:
_timer_connection(env),
_timer(_timer_connection, env.ep())
{ }
Timer(Genode::Env &env) : _timer(env) { }
unsigned long curr_time() const
unsigned long curr_time()
{
return _timer.curr_time().value/1000;
return _timer.curr_time().trunc_to_plain_us().value/1000;
}
static Microseconds microseconds(unsigned long timeout_ms)
@ -188,7 +182,7 @@ struct Libc::Timer
static unsigned long max_timeout()
{
return Genode::Timer::Microseconds::max().value/1000;
return ~0UL/1000;
}
};
@ -217,14 +211,14 @@ struct Libc::Timeout_handler
*/
struct Libc::Timeout
{
Libc::Timer_accessor &_timer_accessor;
Timeout_handler &_handler;
Genode::One_shot_timeout<Timeout> _timeout;
Libc::Timer_accessor &_timer_accessor;
Timeout_handler &_handler;
::Timer::One_shot_timeout<Timeout> _timeout;
bool _expired = true;
unsigned long _absolute_timeout_ms = 0;
void _handle(Microseconds now)
void _handle(Duration now)
{
_expired = true;
_absolute_timeout_ms = 0;
@ -245,7 +239,7 @@ struct Libc::Timeout
_expired = false;
_absolute_timeout_ms = now + timeout_ms;
_timeout.start(_timer_accessor.timer().microseconds(timeout_ms));
_timeout.schedule(_timer_accessor.timer().microseconds(timeout_ms));
}
unsigned long duration_left() const

View File

@ -21,10 +21,6 @@
#ifndef _LIBC__TASK_H_
#define _LIBC__TASK_H_
/* Genode includes */
#include <os/timeout.h>
namespace Libc {
/**

View File

@ -0,0 +1,84 @@
/*
* \brief A duration type for both highly precise and long durations
* \author Martin Stein
* \date 2017-03-21
*/
/*
* 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.
*/
#ifndef _OS__DURATION_H_
#define _OS__DURATION_H_
/* Genode includes */
#include <base/stdint.h>
#include <base/exception.h>
namespace Genode {
class Microseconds;
class Milliseconds;
class Duration;
}
/**
* Makes it clear that a given integer value stands for microseconds
*/
struct Genode::Microseconds
{
unsigned long value;
explicit Microseconds(unsigned long value) : value(value) { }
};
/**
* Makes it clear that a given integer value stands for milliseconds
*/
struct Genode::Milliseconds
{
unsigned long value;
explicit Milliseconds(unsigned long value) : value(value) { }
};
/**
* A duration type that combines high precision and large intervals
*/
struct Genode::Duration
{
public:
struct Overflow : Exception { };
private:
enum { US_PER_MS = 1000UL };
enum { MS_PER_HOUR = 1000UL * 60 * 60 };
enum { US_PER_HOUR = 1000UL * 1000 * 60 * 60 };
unsigned long _microseconds { 0 };
unsigned long _hours { 0 };
void _raise_hours(unsigned long hours);
public:
void operator += (Microseconds us);
void operator += (Milliseconds ms);
bool operator < (Duration &other) const;
explicit Duration(Milliseconds ms) { *this += ms; }
explicit Duration(Microseconds us) { *this += us; }
Microseconds trunc_to_plain_us() const;
};
#endif /* _OS__DURATION_H_ */

View File

@ -1,64 +0,0 @@
/*
* \brief Interface of a time source that can handle one timeout at a time
* \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.
*/
#ifndef _OS__TIME_SOURCE_H_
#define _OS__TIME_SOURCE_H_
namespace Genode { class Time_source; }
/**
* Interface of a time source that can handle one timeout at a time
*/
struct Genode::Time_source
{
/**
* Makes it clear which time unit an interfaces takes
*/
struct Microseconds
{
unsigned long value;
explicit Microseconds(unsigned long const value) : value(value) { }
static Microseconds max() { return Microseconds(~0UL); }
};
/**
* Interface of a timeout callback
*/
struct Timeout_handler
{
virtual void handle_timeout(Microseconds curr_time) = 0;
};
/**
* Return the current time of the source
*/
virtual Microseconds curr_time() const = 0;
/**
* Return the maximum timeout duration that the source can handle
*/
virtual Microseconds max_timeout() const = 0;
/**
* Install a timeout, overrides the last timeout if any
*
* \param duration timeout duration
* \param handler timeout callback
*/
virtual void schedule_timeout(Microseconds duration,
Timeout_handler &handler) = 0;
};
#endif /* _OS__TIME_SOURCE_H_ */

View File

@ -1,99 +0,0 @@
/*
* \brief Multiplexes a timer session amongst different timeouts
* \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.
*/
#ifndef _TIMER_H_
#define _TIMER_H_
/* Genode includes */
#include <timer_session/timer_session.h>
#include <os/time_source.h>
#include <os/timeout.h>
namespace Genode {
class Timer;
class Timer_time_source;
}
/**
* Implementation helper for 'Timer'
*
* \noapi
*/
class Genode::Timer_time_source : public Genode::Time_source
{
private:
enum { MIN_TIMEOUT_US = 5000 };
using Signal_handler = Genode::Io_signal_handler<Timer_time_source>;
::Timer::Session &_session;
Signal_handler _signal_handler;
Timeout_handler *_handler = nullptr;
void _handle_timeout()
{
if (_handler)
_handler->handle_timeout(curr_time());
}
public:
Timer_time_source(::Timer::Session &session, Entrypoint &ep)
:
_session(session),
_signal_handler(ep, *this, &Timer_time_source::_handle_timeout)
{
_session.sigh(_signal_handler);
}
Microseconds curr_time() const {
return Microseconds(1000UL * _session.elapsed_ms()); }
void schedule_timeout(Microseconds duration,
Timeout_handler &handler)
{
if (duration.value < MIN_TIMEOUT_US)
duration.value = MIN_TIMEOUT_US;
if (duration.value > max_timeout().value)
duration.value = max_timeout().value;
_handler = &handler;
_session.trigger_once(duration.value);
}
Microseconds max_timeout() const { return Microseconds::max(); }
};
/**
* Timer-session based timeout scheduler
*
* Multiplexes a timer session amongst different timeouts.
*/
struct Genode::Timer : private Genode::Timer_time_source,
public Genode::Alarm_timeout_scheduler
{
using Time_source::Microseconds;
using Alarm_timeout_scheduler::curr_time;
Timer(::Timer::Session &session, Entrypoint &ep)
:
Timer_time_source(session, ep),
Alarm_timeout_scheduler(*(Time_source*)this)
{ }
};
#endif /* _TIMER_H_ */

View File

@ -2,6 +2,11 @@
* \brief Multiplexing one time source amongst different timeouts
* \author Martin Stein
* \date 2016-11-04
*
* These classes are not meant to be used directly. They merely exist to share
* the generic parts of timeout-scheduling between the Timer::Connection and the
* Timer driver. For user-level timeout-scheduling you should use the interface
* in timer_session/connection.h instead.
*/
/*
@ -11,23 +16,72 @@
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _OS__TIMEOUT_H_
#define _OS__TIMEOUT_H_
#ifndef _TIMER__TIMEOUT_H_
#define _TIMER__TIMEOUT_H_
/* Genode includes */
#include <util/noncopyable.h>
#include <os/time_source.h>
#include <os/alarm.h>
#include <base/log.h>
#include <os/duration.h>
namespace Genode {
class Time_source;
class Timeout_scheduler;
class Timeout;
class Alarm_timeout_scheduler;
template <typename> class Periodic_timeout;
template <typename> class One_shot_timeout;
}
namespace Timer
{
class Connection;
class Root_component;
}
/**
* Interface of a time source that can handle one timeout at a time
*/
struct Genode::Time_source
{
/**
* Interface of a timeout callback
*/
struct Timeout_handler
{
virtual void handle_timeout(Duration curr_time) = 0;
};
/**
* Return the current time of the source
*/
virtual Duration curr_time() = 0;
/**
* Return the maximum timeout duration that the source can handle
*/
virtual Microseconds max_timeout() const = 0;
/**
* Install a timeout, overrides the last timeout if any
*
* \param duration timeout duration
* \param handler timeout callback
*/
virtual void schedule_timeout(Microseconds duration,
Timeout_handler &handler) = 0;
/**
* Tell the time source which scheduler to use for its own timeouts
*
* This method enables a time source for example to synchronize with an
* accurate but expensive timer only on a periodic basis while using a
* cheaper interpolation in general.
*/
virtual void scheduler(Timeout_scheduler &scheduler) { };
};
/**
* Interface of a time-source multiplexer
@ -38,10 +92,6 @@ namespace Genode {
*/
class Genode::Timeout_scheduler
{
public:
using Microseconds = Time_source::Microseconds;
private:
friend Timeout;
@ -74,7 +124,7 @@ class Genode::Timeout_scheduler
/**
* Read out the now time of the scheduler
*/
virtual Microseconds curr_time() const = 0;
virtual Duration curr_time() = 0;
};
@ -97,16 +147,11 @@ class Genode::Timeout : private Noncopyable
*/
struct Handler
{
using Microseconds = Time_source::Microseconds;
virtual void handle_timeout(Microseconds curr_time) = 0;
virtual void handle_timeout(Duration curr_time) = 0;
};
private:
using Microseconds = Time_source::Microseconds;
struct Alarm : Genode::Alarm
{
Timeout_scheduler &timeout_scheduler;
@ -137,98 +182,8 @@ class Genode::Timeout : private Noncopyable
void schedule_one_shot(Microseconds duration, Handler &handler);
void discard();
};
/**
* Periodic timeout that is linked to a custom handler, starts when constructed
*/
template <typename HANDLER>
struct Genode::Periodic_timeout : private Noncopyable
{
public:
using Microseconds = Timeout_scheduler::Microseconds;
private:
typedef void (HANDLER::*Handler_method)(Microseconds);
Timeout _timeout;
struct Handler : Timeout::Handler
{
HANDLER &object;
Handler_method const method;
Handler(HANDLER &object, Handler_method method)
: object(object), method(method) { }
/**********************
** Timeout::Handler **
**********************/
void handle_timeout(Microseconds curr_time) override {
(object.*method)(curr_time); }
} _handler;
public:
Periodic_timeout(Timeout_scheduler &timeout_scheduler,
HANDLER &object,
Handler_method method,
Microseconds duration)
:
_timeout(timeout_scheduler), _handler(object, method)
{
_timeout.schedule_periodic(duration, _handler);
}
};
/**
* One-shot timeout that is linked to a custom handler, started manually
*/
template <typename HANDLER>
class Genode::One_shot_timeout : private Noncopyable
{
private:
using Microseconds = Timeout_scheduler::Microseconds;
typedef void (HANDLER::*Handler_method)(Microseconds);
Timeout _timeout;
struct Handler : Timeout::Handler
{
HANDLER &object;
Handler_method const method;
Handler(HANDLER &object, Handler_method method)
: object(object), method(method) { }
/**********************
** Timeout::Handler **
**********************/
void handle_timeout(Microseconds curr_time) override {
(object.*method)(curr_time); }
} _handler;
public:
One_shot_timeout(Timeout_scheduler &timeout_scheduler,
HANDLER &object,
Handler_method method)
: _timeout(timeout_scheduler), _handler(object, method) { }
void start(Microseconds duration) {
_timeout.schedule_one_shot(duration, _handler); }
bool scheduled() { return _alarm.handler != nullptr; }
};
@ -239,17 +194,22 @@ class Genode::Alarm_timeout_scheduler : private Noncopyable,
public Timeout_scheduler,
public Time_source::Timeout_handler
{
friend class Timer::Connection;
friend class Timer::Root_component;
private:
Time_source &_time_source;
Alarm_scheduler _alarm_scheduler;
void _enable();
/**********************************
** Time_source::Timeout_handler **
**********************************/
void handle_timeout(Microseconds curr_time) override;
void handle_timeout(Duration curr_time) override;
/***********************
@ -271,8 +231,7 @@ class Genode::Alarm_timeout_scheduler : private Noncopyable,
** Timeout_scheduler **
***********************/
Microseconds curr_time() const override {
return _time_source.curr_time(); }
Duration curr_time() override { return _time_source.curr_time(); }
};
#endif /* _OS__TIMEOUT_H_ */
#endif /* _TIMER__TIMEOUT_H_ */

View File

@ -1,5 +1,5 @@
/*
* \brief Connection to timer service
* \brief Connection to timer service and timeout scheduler
* \author Norman Feske
* \date 2008-08-22
*/
@ -14,16 +14,158 @@
#ifndef _INCLUDE__TIMER_SESSION__CONNECTION_H_
#define _INCLUDE__TIMER_SESSION__CONNECTION_H_
/* Genode includes */
#include <timer_session/client.h>
#include <base/connection.h>
#include <util/reconstructible.h>
#include <base/entrypoint.h>
#include <timer/timeout.h>
#include <trace/timestamp.h>
namespace Timer { class Connection; }
namespace Timer
{
class Connection;
template <typename> class Periodic_timeout;
template <typename> class One_shot_timeout;
}
class Timer::Connection : public Genode::Connection<Session>, public Session_client
/**
* Periodic timeout that is linked to a custom handler, scheduled when constructed
*/
template <typename HANDLER>
struct Timer::Periodic_timeout : private Genode::Noncopyable
{
private:
using Duration = Genode::Duration;
using Timeout = Genode::Timeout;
using Timeout_scheduler = Genode::Timeout_scheduler;
using Microseconds = Genode::Microseconds;
typedef void (HANDLER::*Handler_method)(Duration);
Timeout _timeout;
struct Handler : Timeout::Handler
{
HANDLER &object;
Handler_method const method;
Handler(HANDLER &object, Handler_method method)
: object(object), method(method) { }
/**********************
** Timeout::Handler **
**********************/
void handle_timeout(Duration curr_time) override {
(object.*method)(curr_time); }
} _handler;
public:
Periodic_timeout(Timeout_scheduler &timeout_scheduler,
HANDLER &object,
Handler_method method,
Microseconds duration)
:
_timeout(timeout_scheduler), _handler(object, method)
{
_timeout.schedule_periodic(duration, _handler);
}
};
/**
* One-shot timeout that is linked to a custom handler, scheduled manually
*/
template <typename HANDLER>
class Timer::One_shot_timeout : private Genode::Noncopyable
{
private:
using Duration = Genode::Duration;
using Timeout = Genode::Timeout;
using Timeout_scheduler = Genode::Timeout_scheduler;
using Microseconds = Genode::Microseconds;
typedef void (HANDLER::*Handler_method)(Duration);
Timeout _timeout;
struct Handler : Timeout::Handler
{
HANDLER &object;
Handler_method const method;
Handler(HANDLER &object, Handler_method method)
: object(object), method(method) { }
/**********************
** Timeout::Handler **
**********************/
void handle_timeout(Duration curr_time) override {
(object.*method)(curr_time); }
} _handler;
public:
One_shot_timeout(Timeout_scheduler &timeout_scheduler,
HANDLER &object,
Handler_method method)
: _timeout(timeout_scheduler), _handler(object, method) { }
void schedule(Microseconds duration) {
_timeout.schedule_one_shot(duration, _handler); }
void discard() { _timeout.discard(); }
bool scheduled() { return _timeout.scheduled(); }
};
/**
* Connection to timer service and timeout scheduler
*
* Multiplexes a timer session amongst different timeouts.
*/
class Timer::Connection : public Genode::Connection<Session>,
public Session_client,
private Genode::Time_source,
public Genode::Timeout_scheduler
{
private:
using Timeout = Genode::Timeout;
using Timeout_handler = Genode::Time_source::Timeout_handler;
using Timestamp = Genode::Trace::Timestamp;
using Duration = Genode::Duration;
using Lock = Genode::Lock;
using Microseconds = Genode::Microseconds;
using Milliseconds = Genode::Milliseconds;
using Entrypoint = Genode::Entrypoint;
/*
* The mode determines which interface of the timer connection is
* enabled. Initially, a timer connection is in LEGACY mode. When in
* MODERN mode, a call to the LEGACY interface causes an exception.
* When in LEGACY mode, a call to the MODERN interface causes a switch
* to the MODERN mode. The LEGACY interface is deprecated. Please
* prefer using the MODERN interface.
*
* LEGACY = Timer Session interface, blocking calls usleep and msleep
* MODERN = more precise curr_time, non-blocking and multiplexed
* handling with Periodic_timeout resp. One_shot_timeout
*/
enum Mode { LEGACY, MODERN };
Mode _mode { LEGACY };
Genode::Lock _lock;
Genode::Signal_receiver _sig_rec;
Genode::Signal_context _default_sigh_ctx;
@ -33,21 +175,88 @@ class Timer::Connection : public Genode::Connection<Session>, public Session_cli
Genode::Signal_context_capability _custom_sigh_cap;
void _enable_modern_mode();
void _sigh(Signal_context_capability sigh)
{
Session_client::sigh(sigh);
}
/*************************
** Time_source helpers **
*************************/
/*
* The higher the factor shift, the more precise is the time
* interpolation but the more likely it becomes that an overflow
* would occur during calculations. In this case, the timer
* down-scales the values live which is avoidable overhead.
*/
enum { TS_TO_US_RATIO_SHIFT = 4 };
enum { MIN_TIMEOUT_US = 5000 };
enum { REAL_TIME_UPDATE_PERIOD_US = 500000 };
enum { MAX_TS = ~(Timestamp)0ULL >> TS_TO_US_RATIO_SHIFT };
enum { MAX_INTERPOLATION_QUALITY = 3 };
enum { MAX_REMOTE_TIME_LATENCY_US = 500 };
enum { MAX_REMOTE_TIME_TRIALS = 5 };
Genode::Io_signal_handler<Connection> _signal_handler;
Timeout_handler *_handler { nullptr };
Lock _real_time_lock { Lock::UNLOCKED };
unsigned long _ms { elapsed_ms() };
Timestamp _ts { _timestamp() };
Duration _real_time { Milliseconds(_ms) };
Duration _interpolated_time { _real_time };
unsigned _interpolation_quality { 0 };
unsigned long _us_to_ts_factor { 1UL << TS_TO_US_RATIO_SHIFT };
Timestamp _timestamp();
void _update_interpolation_quality(unsigned long min_factor,
unsigned long max_factor);
unsigned long _ts_to_us_ratio(Timestamp ts, unsigned long us);
void _update_real_time();
Duration _update_interpolated_time(Duration &interpolated_time);
void _handle_timeout();
/*****************
** Time_source **
*****************/
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
Microseconds max_timeout() const override { return Microseconds(REAL_TIME_UPDATE_PERIOD_US); }
/*******************************
** Timeout_scheduler helpers **
*******************************/
Genode::Alarm_timeout_scheduler _scheduler { *this };
/***********************
** Timeout_scheduler **
***********************/
void _schedule_one_shot(Timeout &timeout, Microseconds duration) override;
void _schedule_periodic(Timeout &timeout, Microseconds duration) override;
void _discard(Timeout &timeout) override;
public:
struct Cannot_use_both_legacy_and_modern_interface : Genode::Exception { };
/**
* Constructor
*/
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())
{
/* register default signal handler */
Session_client::sigh(_default_sigh_cap);
}
Connection(Genode::Env &env, char const *label = "");
/**
* Constructor
@ -56,28 +265,36 @@ class Timer::Connection : public Genode::Connection<Session>, public Session_cli
* \deprecated Use the constructor with 'Env &' as first
* argument instead
*/
Connection() __attribute__((deprecated))
:
Genode::Connection<Session>(session("ram_quota=10K")),
Session_client(cap())
{
/* register default signal handler */
Session_client::sigh(_default_sigh_cap);
}
Connection() __attribute__((deprecated));
~Connection() { _sig_rec.dissolve(&_default_sigh_ctx); }
/*
* Intercept 'sigh' to keep track of customized signal handlers
*
* \noapi
* \deprecated Use One_shot_timeout (or Periodic_timeout) instead
*/
void sigh(Signal_context_capability sigh)
void sigh(Signal_context_capability sigh) override
{
if (_mode == MODERN) {
throw Cannot_use_both_legacy_and_modern_interface();
}
_custom_sigh_cap = sigh;
Session_client::sigh(_custom_sigh_cap);
}
/*
* Block for a time span of 'us' microseconds
*
* \noapi
* \deprecated Use One_shot_timeout (or Periodic_timeout) instead
*/
void usleep(unsigned us)
{
if (_mode == MODERN) {
throw Cannot_use_both_legacy_and_modern_interface();
}
/*
* Omit the interaction with the timer driver for the corner case
* of not sleeping at all. This corner case may be triggered when
@ -105,10 +322,26 @@ class Timer::Connection : public Genode::Connection<Session>, public Session_cli
Session_client::sigh(_custom_sigh_cap);
}
/*
* Block for a time span of 'ms' milliseconds
*
* \noapi
* \deprecated Use One_shot_timeout (or Periodic_timeout) instead
*/
void msleep(unsigned ms)
{
if (_mode == MODERN) {
throw Cannot_use_both_legacy_and_modern_interface();
}
usleep(1000*ms);
}
/***********************************
** Timeout_scheduler/Time_source **
***********************************/
Duration curr_time() override;
};
#endif /* _INCLUDE__TIMER_SESSION__CONNECTION_H_ */

View File

@ -1,5 +1,10 @@
SRC_CC += timeout.cc
SRC_CC += timer_connection.cc
SRC_CC += timer_connection_timestamp.cc
SRC_CC += duration.cc
LIBS += alarm
INC_DIR += $(BASE_DIR)/src/include
vpath % $(REP_DIR)/src/lib/timeout

View File

@ -2,7 +2,28 @@
# Build
#
build "core init drivers/timer test/timeout"
#
# Do not run on QEMU as its time emulation is not precise enough
#
if {[get_cmd_switch --autopilot] && [have_include "power_on/qemu"]} {
puts "\nRunning timeout test in autopilot on Qemu is not recommended.\n"
exit 0
}
#
# Do not run on ARM with kernels other than HW
#
# The ARM performance counter has no reliable frequency as the ARM idle command
# (often called on idle) halts the counter. Only on the HW kernel we have a syscall
# that enables us to avoid the use of the performance counter by reading the kernel
# time instead.
#
if {[expr [have_spec arm] && ![have_spec hw]]} {
puts "\n Run script is not supported on this platform.\n";
exit 0
}
build "core init drivers/platform drivers/timer test/timeout test/cpufreq"
#
# Boot image
@ -33,7 +54,7 @@ install_config {
</start>
<start name="test">
<binary name="test-timeout"/>
<resource name="RAM" quantum="10M"/>
<resource name="RAM" quantum="250M"/>
</start>
</config>
}
@ -44,30 +65,8 @@ build_boot_image "core ld.lib.so init timer test-timeout"
# Execution
#
append qemu_args "-nographic -m 64"
append qemu_args "-nographic -m 350"
#
# We check for each timeout that has a distance of at least 200ms to each
# other timeout:
#
# 0 ms
# 0 ms
# 700 ms -> check for 700 ms
# 1000 ms -> check for 1000 ms
# 1400 ms -> check for 700 ms
# 2000 ms
# 2100 ms
# 2800 ms -> check for 700 ms
# 3000 ms -> check for 1000 ms
# 3250 ms -> check for 3250 ms
# 3500 ms -> check for 700 ms
# 4000 ms -> check for 1000 ms
# 4200 ms -> check for 700 ms
# 4900 ms
# 5000 ms
# 5200 ms -> check for 5200 ms
# 5600 ms -> check for 700 ms
# 6000 ms -> check for 1000 ms
#
run_genode_until ".*700ms timeout.*\n.*1000ms timeout.*\n.*700ms timeout.*\n.*700ms timeout.*\n.*1000ms timeout.*\n.*3250ms timeout.*\n.*700ms timeout.*\n.*1000ms timeout.*\n.*700ms timeout.*\n.*5200ms timeout.*\n.*700ms timeout.*\n.*1000ms timeout.*\n" 20
run_genode_until "child \"test\" exited with exit value.*\n" 900
grep_output {\[init\] child "test" exited with exit value}
compare_output_to {[init] child "test" exited with exit value 0}

View File

@ -39,7 +39,8 @@ namespace Fiasco {
#include <time_source.h>
using namespace Fiasco;
using Microseconds = Genode::Time_source::Microseconds;
using Microseconds = Genode::Microseconds;
using Duration = Genode::Duration;
static l4_timeout_s mus_to_timeout(unsigned long mus)
@ -71,14 +72,14 @@ Microseconds Timer::Time_source::max_timeout() const
}
Microseconds Timer::Time_source::curr_time() const
Duration Timer::Time_source::curr_time()
{
Genode::Lock::Guard lock_guard(_lock);
static Genode::Attached_rom_dataspace kip_ds(_env, "l4v2_kip");
static Fiasco::l4_kernel_info_t * const kip =
kip_ds.local_addr<Fiasco::l4_kernel_info_t>();
return Microseconds(kip->clock);
return Duration(Microseconds(kip->clock));
}

View File

@ -23,7 +23,6 @@
#include <kernel/interface.h>
using namespace Genode;
using Microseconds = Genode::Time_source::Microseconds;
enum { MIN_TIMEOUT_US = 1000 };
@ -56,14 +55,14 @@ void Timer::Time_source::schedule_timeout(Microseconds duration,
}
Microseconds Timer::Time_source::curr_time() const
Duration Timer::Time_source::curr_time()
{
unsigned long const timeout_age_us = Kernel::timeout_age_us();
if (timeout_age_us > _last_timeout_age_us) {
/* increment time by the difference since the last update */
_curr_time_us += timeout_age_us - _last_timeout_age_us;
_curr_time += Microseconds(timeout_age_us - _last_timeout_age_us);
_last_timeout_age_us = timeout_age_us;
}
return Microseconds(_curr_time_us);
return _curr_time;
}

View File

@ -14,17 +14,26 @@
#ifndef _TIME_SOURCE_H_
#define _TIME_SOURCE_H_
/* Genode includes */
#include <os/duration.h>
/* local includes */
#include <signalled_time_source.h>
namespace Timer { class Time_source; }
namespace Timer {
using Microseconds = Genode::Microseconds;
using Duration = Genode::Duration;
class Time_source;
}
class Timer::Time_source : public Genode::Signalled_time_source
{
private:
unsigned long mutable _curr_time_us = 0;
Duration mutable _curr_time { Microseconds(0) };
unsigned long mutable _last_timeout_age_us = 0;
unsigned long const _max_timeout_us;
@ -37,7 +46,7 @@ class Timer::Time_source : public Genode::Signalled_time_source
** Genode::Time_source **
*************************/
Microseconds curr_time() const override;
Duration curr_time() override;
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
Microseconds max_timeout() const override {
return Microseconds(_max_timeout_us); };

View File

@ -56,7 +56,9 @@ class Timer::Root_component : public Genode::Root_component<Session_component>
:
Genode::Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
_time_source(env), _timeout_scheduler(_time_source)
{ }
{
_timeout_scheduler._enable();
}
};
#endif /* _ROOT_COMPONENT_H_ */

View File

@ -21,9 +21,14 @@
#include <util/list.h>
#include <timer_session/timer_session.h>
#include <base/rpc_server.h>
#include <os/timeout.h>
#include <timer/timeout.h>
namespace Timer { class Session_component; }
namespace Timer {
using Microseconds = Genode::Microseconds;
using Duration = Genode::Duration;
class Session_component;
}
class Timer::Session_component : public Genode::Rpc_object<Session>,
@ -36,9 +41,10 @@ class Timer::Session_component : public Genode::Rpc_object<Session>,
Genode::Timeout_scheduler &_timeout_scheduler;
Genode::Signal_context_capability _sigh;
unsigned long const _init_time_us = _timeout_scheduler.curr_time().value;
unsigned long const _init_time_us =
_timeout_scheduler.curr_time().trunc_to_plain_us().value;
void handle_timeout(Microseconds) {
void handle_timeout(Duration) {
Genode::Signal_transmitter(_sigh).submit(); }
public:
@ -65,7 +71,8 @@ class Timer::Session_component : public Genode::Rpc_object<Session>,
}
unsigned long elapsed_ms() const override {
return (_timeout_scheduler.curr_time().value - _init_time_us) / 1000; }
return (_timeout_scheduler.curr_time().trunc_to_plain_us().value -
_init_time_us) / 1000; }
void msleep(unsigned) override { /* never called at the server side */ }
void usleep(unsigned) override { /* never called at the server side */ }

View File

@ -16,7 +16,7 @@
/* Genode includes */
#include <base/env.h>
#include <os/time_source.h>
#include <timer/timeout.h>
#include <base/rpc_client.h>
namespace Genode { class Signalled_time_source; }

View File

@ -18,10 +18,12 @@
/* Genode inludes */
#include <base/env.h>
#include <base/rpc_client.h>
#include <os/time_source.h>
#include <timer/timeout.h>
namespace Timer {
using Genode::Microseconds;
using Genode::Duration;
class Threaded_time_source;
}
@ -33,7 +35,7 @@ class Timer::Threaded_time_source : public Genode::Time_source,
struct Irq_dispatcher
{
GENODE_RPC(Rpc_do_dispatch, void, do_dispatch, Microseconds);
GENODE_RPC(Rpc_do_dispatch, void, do_dispatch, Duration);
GENODE_RPC_INTERFACE(Rpc_do_dispatch);
};
@ -47,10 +49,10 @@ class Timer::Threaded_time_source : public Genode::Time_source,
** Irq_dispatcher **
********************/
void do_dispatch(Microseconds duration)
void do_dispatch(Duration curr_time)
{
if (handler) {
handler->handle_timeout(Microseconds(duration)); }
handler->handle_timeout(Duration(curr_time)); }
}
} _irq_dispatcher_component;

View File

@ -20,7 +20,6 @@
#include <time_source.h>
using namespace Genode;
using Microseconds = Genode::Time_source::Microseconds;
inline int lx_gettimeofday(struct timeval *tv, struct timeval *tz) {
@ -34,11 +33,11 @@ Microseconds Timer::Time_source::max_timeout() const
}
Microseconds Timer::Time_source::curr_time() const
Duration Timer::Time_source::curr_time()
{
struct timeval tv;
lx_gettimeofday(&tv, 0);
return Microseconds(tv.tv_sec * 1000 * 1000 + tv.tv_usec);
return Duration(Microseconds(tv.tv_sec * 1000 * 1000 + tv.tv_usec));
}

View File

@ -23,7 +23,6 @@
using namespace Genode;
using namespace Nova;
using Microseconds = Genode::Time_source::Microseconds;
Timer::Time_source::Time_source(Env &env) : Threaded_time_source(env)

View File

@ -64,8 +64,8 @@ class Timer::Time_source : public Threaded_time_source
Microseconds max_timeout() const override { return _tsc_to_us(~0UL); }
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
Microseconds curr_time() const override {
return _tsc_to_us(Genode::Trace::timestamp()); }
Duration curr_time() override {
return Duration(_tsc_to_us(Genode::Trace::timestamp())); }
};
#endif /* _TIME_SOURCE_H_ */

View File

@ -30,7 +30,7 @@ void Timer::Time_source::schedule_timeout(Microseconds duration,
void Timer::Time_source::_wait_for_irq()
{
enum { SLEEP_GRANULARITY_US = 1000UL };
unsigned long last_time_us = curr_time().value;
unsigned long last_time_us = curr_time().trunc_to_plain_us().value;
_lock.lock();
while (_next_timeout_us > 0) {
_lock.unlock();
@ -38,7 +38,7 @@ void Timer::Time_source::_wait_for_irq()
try { _usleep(SLEEP_GRANULARITY_US); }
catch (Blocking_canceled) { }
unsigned long curr_time_us = curr_time().value;
unsigned long curr_time_us = curr_time().trunc_to_plain_us().value;
unsigned long sleep_duration_us = curr_time_us - last_time_us;
last_time_us = curr_time_us;

View File

@ -50,7 +50,7 @@ class Timer::Time_source : public Threaded_time_source
** Genode::Time_source **
*************************/
Microseconds curr_time() const override;
Duration curr_time() override;
Microseconds max_timeout() const override;
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
};

View File

@ -16,7 +16,6 @@
#include <time_source.h>
using namespace Genode;
using Microseconds = Genode::Time_source::Microseconds;
void Timer::Time_source::_set_counter(uint16_t value)
@ -67,7 +66,7 @@ void Timer::Time_source::schedule_timeout(Microseconds duration,
}
Microseconds Timer::Time_source::curr_time() const
Duration Timer::Time_source::curr_time()
{
uint32_t passed_ticks;
@ -100,7 +99,7 @@ Microseconds Timer::Time_source::curr_time() const
/* use current counter as the reference for the next update */
_counter_init_value = curr_counter;
return Microseconds(_curr_time_us);
return Duration(Microseconds(_curr_time_us));
}

View File

@ -18,11 +18,17 @@
/* Genode includes */
#include <io_port_session/connection.h>
#include <irq_session/connection.h>
#include <os/duration.h>
/* local includes */
#include <signalled_time_source.h>
namespace Timer { class Time_source; }
namespace Timer {
using Microseconds = Genode::Microseconds;
using Duration = Genode::Duration;
class Time_source;
}
class Timer::Time_source : public Genode::Signalled_time_source
@ -81,7 +87,7 @@ class Timer::Time_source : public Genode::Signalled_time_source
** Genode::Time_source **
*************************/
Microseconds curr_time() const override;
Duration curr_time() override;
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
Microseconds max_timeout() const override {
return Microseconds(PIT_MAX_USEC); }

View File

@ -1,5 +1,5 @@
SRC_CC += main.cc
LIBS += base timeout
LIBS += base
INC_DIR += $(REP_DIR)/src/drivers/timer/include
vpath %.cc $(REP_DIR)/src/drivers/timer

View File

@ -0,0 +1,67 @@
/*
* \brief A duration type for both highly precise and long durations
* \author Martin Stein
* \date 2017-03-21
*/
/*
* 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 <os/duration.h>
using namespace Genode;
void Duration::_raise_hours(unsigned long hours)
{
unsigned long const old_hours = _hours;
_hours += hours;
if (_hours < old_hours) {
throw Overflow(); }
}
void Duration::operator += (Microseconds us)
{
_microseconds += us.value;
if (_microseconds > US_PER_HOUR) {
_microseconds -= US_PER_HOUR;
_raise_hours(1);
}
}
void Duration::operator += (Milliseconds ms)
{
/* filter out hours if any */
if (ms.value >= MS_PER_HOUR) {
unsigned long const hours = ms.value / MS_PER_HOUR;
_raise_hours(hours);
ms.value -= hours * MS_PER_HOUR;
}
/* add the rest as microseconds value */
*this += Microseconds(ms.value * US_PER_MS);
}
bool Duration::operator < (Duration &other) const
{
if (_hours != other._hours) {
return _hours < other._hours; }
if (_microseconds != other._microseconds) {
return _microseconds < other._microseconds; }
return false;
}
Microseconds Duration::trunc_to_plain_us() const
{
return Microseconds(_microseconds + (_hours ? _hours * US_PER_HOUR : 0));
}

View File

@ -0,0 +1,24 @@
/*
* \brief Timestamp implementation for the Genode Timer
* \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 <kernel/interface.h>
#include <timer_session/connection.h>
using namespace Genode;
Trace::Timestamp Timer::Connection::_timestamp()
{
return Kernel::time();
}

View File

@ -12,7 +12,7 @@
*/
/* Genode includes */
#include <os/timeout.h>
#include <timer/timeout.h>
using namespace Genode;
@ -28,6 +28,7 @@ void Timeout::schedule_periodic(Microseconds duration, Handler &handler)
_alarm.timeout_scheduler._schedule_periodic(*this, duration);
}
void Timeout::schedule_one_shot(Microseconds duration, Handler &handler)
{
_alarm.handler = &handler;
@ -35,6 +36,7 @@ void Timeout::schedule_one_shot(Microseconds duration, Handler &handler)
_alarm.timeout_scheduler._schedule_one_shot(*this, duration);
}
void Timeout::discard()
{
_alarm.timeout_scheduler._discard(*this);
@ -49,24 +51,29 @@ void Timeout::discard()
bool Timeout::Alarm::on_alarm(unsigned)
{
if (handler) {
handler->handle_timeout(timeout_scheduler.curr_time()); }
Handler *current = handler;
if (!periodic) {
handler = nullptr;
}
current->handle_timeout(timeout_scheduler.curr_time());
}
return periodic;
}
/*****************************
** Alarm_timeout_scheduler **
*****************************/
void Alarm_timeout_scheduler::handle_timeout(Microseconds curr_time)
void Alarm_timeout_scheduler::handle_timeout(Duration curr_time)
{
_alarm_scheduler.handle(curr_time.value);
unsigned long const curr_time_us = curr_time.trunc_to_plain_us().value;
_alarm_scheduler.handle(curr_time_us);
unsigned long sleep_time_us;
Alarm::Time deadline_us;
if (_alarm_scheduler.next_deadline(&deadline_us)) {
sleep_time_us = deadline_us - curr_time.value;
sleep_time_us = deadline_us - curr_time_us;
} else {
sleep_time_us = _time_source.max_timeout().value; }
@ -83,17 +90,21 @@ void Alarm_timeout_scheduler::handle_timeout(Microseconds curr_time)
Alarm_timeout_scheduler::Alarm_timeout_scheduler(Time_source &time_source)
:
_time_source(time_source)
{ }
void Alarm_timeout_scheduler::_enable()
{
time_source.schedule_timeout(Microseconds(0), *this);
_time_source.schedule_timeout(Microseconds(0), *this);
}
void Alarm_timeout_scheduler::_schedule_one_shot(Timeout &timeout,
Microseconds duration)
{
_alarm_scheduler.schedule_absolute(&timeout._alarm,
_time_source.curr_time().value +
duration.value);
_alarm_scheduler.schedule_absolute(
&timeout._alarm,
_time_source.curr_time().trunc_to_plain_us().value + duration.value);
if (_alarm_scheduler.head_timeout(&timeout._alarm)) {
_time_source.schedule_timeout(Microseconds(0), *this); }
@ -103,7 +114,7 @@ void Alarm_timeout_scheduler::_schedule_one_shot(Timeout &timeout,
void Alarm_timeout_scheduler::_schedule_periodic(Timeout &timeout,
Microseconds duration)
{
_alarm_scheduler.handle(_time_source.curr_time().value);
_alarm_scheduler.handle(_time_source.curr_time().trunc_to_plain_us().value);
_alarm_scheduler.schedule(&timeout._alarm, duration.value);
if (_alarm_scheduler.head_timeout(&timeout._alarm)) {
_time_source.schedule_timeout(Microseconds(0), *this); }

View File

@ -0,0 +1,268 @@
/*
* \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;
}
void Timer::Connection::_update_real_time()
{
Lock_guard<Lock> lock_guard(_real_time_lock);
Timestamp ts = 0UL;
unsigned long ms = 0UL;
unsigned long us_diff = ~0UL;
for (unsigned remote_time_trials = 0;
remote_time_trials < MAX_REMOTE_TIME_TRIALS;
remote_time_trials++)
{
/* determine time and timestamp difference since the last call */
Timestamp volatile new_ts = _timestamp();
unsigned long volatile new_ms = elapsed_ms();
if (_interpolation_quality < MAX_INTERPOLATION_QUALITY) {
ms = new_ms;
ts = new_ts;
break;
}
Timestamp const ts_diff = _timestamp() - new_ts;
unsigned long const new_us_diff = _ts_to_us_ratio(ts_diff,
_us_to_ts_factor);
/* remember results only if the latency was better than last time */
if (new_us_diff < us_diff) {
ms = new_ms;
ts = new_ts;
if (us_diff < MAX_REMOTE_TIME_LATENCY_US) {
break;
}
}
}
unsigned long const ms_diff = ms - _ms;
Timestamp const ts_diff = ts - _ts;
/* update real time and values for next difference calculation */
_ms = ms;
_ts = ts;
_real_time += Milliseconds(ms_diff);
unsigned long const new_factor = _ts_to_us_ratio(ts_diff, ms_diff * 1000UL);
unsigned long const old_factor = _us_to_ts_factor;
/* update interpolation-quality value */
if (old_factor > new_factor) { _update_interpolation_quality(new_factor, old_factor); }
else { _update_interpolation_quality(old_factor, new_factor); }
_us_to_ts_factor = new_factor;
}
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);
}
Duration Timer::Connection::curr_time()
{
_enable_modern_mode();
Reconstructible<Lock_guard<Lock> > lock_guard(_real_time_lock);
Duration interpolated_time(_real_time);
/*
* Interpolate with timestamps only if the factor value
* remained stable for some time. If we would interpolate with
* a yet unstable factor, there's an increased risk that the
* interpolated time falsely reaches an enourmous level. Then
* the value would stand still for quite some time because we
* can't let it jump back to a more realistic level. This
* would also eliminate updates of the real time as the
* timeout scheduler that manages our update timeout also
* uses this function.
*/
if (_interpolation_quality == MAX_INTERPOLATION_QUALITY) {
/* locally buffer real-time related members */
unsigned long const ts = _ts;
unsigned long const us_to_ts_factor = _us_to_ts_factor;
lock_guard.destruct();
/* get time difference since the last real time update */
Timestamp const ts_diff = _timestamp() - ts;
unsigned long const us_diff = _ts_to_us_ratio(ts_diff, us_to_ts_factor);
interpolated_time += Microseconds(us_diff);
} else {
/*
* Use remote timer instead of timestamps
*/
interpolated_time += Milliseconds(elapsed_ms() - _ms);
lock_guard.destruct();
}
return _update_interpolated_time(interpolated_time);
}
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);
}

View File

@ -0,0 +1,24 @@
/*
* \brief Timestamp implementation for the Genode Timer
* \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 <trace/timestamp.h>
#include <timer_session/connection.h>
using namespace Genode;
Trace::Timestamp Timer::Connection::_timestamp()
{
return Trace::timestamp();
}

View File

@ -333,28 +333,28 @@ class Input_filter::Chargen_source : public Source, Source::Sink
*/
struct Char_repeater
{
Source::Sink &_destination;
Genode::Timer &_timer;
Source::Sink &_destination;
Timer::Connection &_timer;
Time_source::Microseconds const _delay;
Time_source::Microseconds const _rate;
Microseconds const _delay;
Microseconds const _rate;
Input::Event::Utf8 _curr_character { 0 };
enum State { IDLE, REPEAT } _state;
void _handle_timeout(Time_source::Microseconds)
void _handle_timeout(Duration)
{
if (_state == REPEAT) {
_destination.submit_event(Input::Event(_curr_character));
_timeout.start(_rate);
_timeout.schedule(_rate);
}
}
One_shot_timeout<Char_repeater> _timeout {
Timer::One_shot_timeout<Char_repeater> _timeout {
_timer, *this, &Char_repeater::_handle_timeout };
Char_repeater(Source::Sink &destination, Genode::Timer &timer,
Char_repeater(Source::Sink &destination, Timer::Connection &timer,
Xml_node node)
:
_destination(destination), _timer(timer),
@ -367,7 +367,7 @@ class Input_filter::Chargen_source : public Source, Source::Sink
_curr_character = character;
_state = REPEAT;
_timeout.start(_delay);
_timeout.schedule(_delay);
}
void cancel()

View File

@ -52,10 +52,9 @@ struct Input_filter::Main : Input_connection::Avail_handler,
{
struct Lazy
{
Timer::Connection connection;
Genode::Timer timer;
Timer::Connection timer;
Lazy(Env &env) : connection(env), timer(connection, env.ep()) { }
Lazy(Env &env) : timer(env) { }
};
Env &_env;
@ -67,7 +66,7 @@ struct Input_filter::Main : Input_connection::Avail_handler,
/**
* Timer_accessor interface
*/
Genode::Timer &timer() override
Timer::Connection &timer() override
{
if (!lazy.constructed())
lazy.construct(_env);

View File

@ -1,4 +1,4 @@
TARGET = input_filter
SRC_CC = main.cc
LIBS = base timeout
LIBS = base
INC_DIR += $(PRG_DIR)

View File

@ -15,10 +15,10 @@
#define _INPUT_FILTER__TIMER_ACCESSOR_H_
/* Genode includes */
#include <os/timer.h>
#include <timer_session/connection.h>
namespace Input_filter { struct Timer_accessor; }
struct Input_filter::Timer_accessor { virtual Genode::Timer &timer() = 0; };
struct Input_filter::Timer_accessor { virtual Timer::Connection &timer() = 0; };
#endif /* _INPUT_FILTER__TIMER_ACCESSOR_H_ */

View File

@ -54,17 +54,17 @@ Session_component_base(Allocator &guarded_alloc_backing,
** Session_component **
***********************/
Net::Session_component::Session_component(Allocator &alloc,
size_t const amount,
Ram_session &buf_ram,
size_t const tx_buf_size,
size_t const rx_buf_size,
Region_map &region_map,
Uplink &uplink,
Xml_node config,
Genode::Timer &timer,
unsigned &curr_time,
Entrypoint &ep)
Net::Session_component::Session_component(Allocator &alloc,
size_t const amount,
Ram_session &buf_ram,
size_t const tx_buf_size,
size_t const rx_buf_size,
Region_map &region_map,
Uplink &uplink,
Xml_node config,
Timer::Connection &timer,
unsigned &curr_time,
Entrypoint &ep)
:
Session_component_base(alloc, amount, buf_ram, tx_buf_size, rx_buf_size),
Session_rpc_object(region_map, _tx_buf, _rx_buf, &_range_alloc, ep.rpc_ep()),
@ -99,14 +99,14 @@ void Session_component::link_state_sigh(Signal_context_capability sigh)
** Root **
**********/
Net::Root::Root(Entrypoint &ep,
Allocator &alloc,
Uplink &uplink,
Ram_session &buf_ram,
Xml_node config,
Genode::Timer &timer,
unsigned &curr_time,
Region_map &region_map)
Net::Root::Root(Entrypoint &ep,
Allocator &alloc,
Uplink &uplink,
Ram_session &buf_ram,
Xml_node config,
Timer::Connection &timer,
unsigned &curr_time,
Region_map &region_map)
:
Root_component<Session_component, Genode::Single_client>(&ep.rpc_ep(),
&alloc),

View File

@ -93,7 +93,7 @@ class Net::Session_component : public Session_component_base,
Genode::Region_map &region_map,
Uplink &uplink,
Genode::Xml_node config,
Genode::Timer &timer,
Timer::Connection &timer,
unsigned &curr_time,
Genode::Entrypoint &ep);
@ -118,7 +118,7 @@ class Net::Root : public Genode::Root_component<Session_component,
Genode::Ram_session &_buf_ram;
Genode::Region_map &_region_map;
Genode::Xml_node _config;
Genode::Timer &_timer;
Timer::Connection &_timer;
unsigned &_curr_time;
@ -135,7 +135,7 @@ class Net::Root : public Genode::Root_component<Session_component,
Uplink &uplink,
Genode::Ram_session &buf_ram,
Genode::Xml_node config,
Genode::Timer &timer,
Timer::Connection &timer,
unsigned &curr_time,
Genode::Region_map &region_map);
};

View File

@ -28,7 +28,7 @@ void Interface::_handle_eth(void *const eth_base,
try {
Ethernet_frame &eth = *new (eth_base) Ethernet_frame(eth_size);
Interface &remote = _remote.deref();
unsigned new_time = _timer.curr_time().value / 1000;
unsigned new_time = _timer.curr_time().trunc_to_plain_us().value / 1000;
if (_log_time) {
log("\033[33m(", remote._label, " <- ", _label, ")\033[0m ", eth,
" \033[33mtime ", new_time, " (", new_time - _curr_time,
@ -86,12 +86,12 @@ void Interface::_ready_to_ack()
}
Interface::Interface(Entrypoint &ep,
Interface_label label,
Genode::Timer &timer,
unsigned &curr_time,
bool log_time,
Allocator &alloc)
Interface::Interface(Entrypoint &ep,
Interface_label label,
Timer::Connection &timer,
unsigned &curr_time,
bool log_time,
Allocator &alloc)
:
_sink_ack (ep, *this, &Interface::_ack_avail),
_sink_submit (ep, *this, &Interface::_ready_to_submit),

View File

@ -20,7 +20,7 @@
/* Genode includes */
#include <nic_session/nic_session.h>
#include <util/string.h>
#include <os/timer.h>
#include <timer_session/connection.h>
namespace Net {
@ -49,7 +49,7 @@ class Net::Interface
Genode::Allocator &_alloc;
Pointer<Interface> _remote;
Interface_label _label;
Genode::Timer &_timer;
Timer::Connection &_timer;
unsigned &_curr_time;
bool _log_time;
@ -77,7 +77,7 @@ class Net::Interface
Interface(Genode::Entrypoint &ep,
Interface_label label,
Genode::Timer &timer,
Timer::Connection &timer,
unsigned &curr_time,
bool log_time,
Genode::Allocator &alloc);

View File

@ -30,8 +30,7 @@ class Main
private:
Genode::Attached_rom_dataspace _config;
Timer::Connection _timer_connection;
Genode::Timer _timer;
Timer::Connection _timer;
unsigned _curr_time { 0 };
Genode::Heap _heap;
Uplink _uplink;
@ -45,8 +44,7 @@ class Main
Main::Main(Env &env)
:
_config(env, "config"), _timer_connection(env),
_timer(_timer_connection, env.ep()), _heap(&env.ram(), &env.rm()),
_config(env, "config"), _timer(env), _heap(&env.ram(), &env.rm()),
_uplink(env, _config.xml(), _timer, _curr_time, _heap),
_root(env.ep(), _heap, _uplink, env.ram(), _config.xml(), _timer,
_curr_time, env.rm())

View File

@ -1,6 +1,6 @@
TARGET = nic_dump
LIBS += base net config timeout
LIBS += base net config
SRC_CC += component.cc main.cc uplink.cc interface.cc

View File

@ -22,11 +22,11 @@ using namespace Net;
using namespace Genode;
Net::Uplink::Uplink(Env &env,
Xml_node config,
Genode::Timer &timer,
unsigned &curr_time,
Allocator &alloc)
Net::Uplink::Uplink(Env &env,
Xml_node config,
Timer::Connection &timer,
unsigned &curr_time,
Allocator &alloc)
:
Nic::Packet_allocator(&alloc),
Nic::Connection(env, this, BUF_SIZE, BUF_SIZE),

View File

@ -46,7 +46,7 @@ class Net::Uplink : public Nic::Packet_allocator,
Uplink(Genode::Env &env,
Genode::Xml_node config,
Genode::Timer &timer,
Timer::Connection &timer,
unsigned &curr_time,
Genode::Allocator &alloc);
};

View File

@ -55,7 +55,7 @@ Session_component_base(Allocator &guarded_alloc_backing,
***********************/
Net::Session_component::Session_component(Allocator &alloc,
Genode::Timer &timer,
Timer::Connection &timer,
size_t const amount,
Ram_session &buf_ram,
size_t const tx_buf_size,
@ -95,7 +95,7 @@ void Session_component::link_state_sigh(Signal_context_capability sigh)
**********/
Net::Root::Root(Entrypoint &ep,
Genode::Timer &timer,
Timer::Connection &timer,
Allocator &alloc,
Mac_address const &router_mac,
Configuration &config,

View File

@ -84,7 +84,7 @@ class Net::Session_component : public Session_component_base,
public:
Session_component(Genode::Allocator &alloc,
Genode::Timer &timer,
Timer::Connection &timer,
Genode::size_t const amount,
Genode::Ram_session &buf_ram,
Genode::size_t const tx_buf_size,
@ -110,7 +110,7 @@ class Net::Root : public Genode::Root_component<Session_component>
{
private:
Genode::Timer &_timer;
Timer::Connection &_timer;
Mac_allocator _mac_alloc;
Genode::Entrypoint &_ep;
Mac_address const _router_mac;
@ -128,7 +128,7 @@ class Net::Root : public Genode::Root_component<Session_component>
public:
Root(Genode::Entrypoint &ep,
Genode::Timer &timer,
Timer::Connection &timer,
Genode::Allocator &alloc,
Mac_address const &router_mac,
Configuration &config,

View File

@ -570,7 +570,7 @@ void Interface::_send(Ethernet_frame &eth, Genode::size_t const size)
Interface::Interface(Entrypoint &ep,
Genode::Timer &timer,
Timer::Connection &timer,
Mac_address const router_mac,
Genode::Allocator &alloc,
Mac_address const mac,

View File

@ -52,7 +52,7 @@ class Net::Interface
private:
Genode::Timer &_timer;
Timer::Connection &_timer;
Genode::Allocator &_alloc;
Domain &_domain;
Arp_cache _arp_cache;
@ -150,7 +150,7 @@ class Net::Interface
struct Packet_postponed : Genode::Exception { };
Interface(Genode::Entrypoint &ep,
Genode::Timer &timer,
Timer::Connection &timer,
Mac_address const router_mac,
Genode::Allocator &alloc,
Mac_address const mac,

View File

@ -115,7 +115,7 @@ Link::Link(Interface &cln_interface,
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Timer::Connection &timer,
Configuration &config,
uint8_t const protocol)
:
@ -127,11 +127,11 @@ Link::Link(Interface &cln_interface,
_close_timeout_us(_config.rtt_sec() * 2 * 1000 * 1000),
_protocol(protocol)
{
_close_timeout.start(_close_timeout_us);
_close_timeout.schedule(_close_timeout_us);
}
void Link::_handle_close_timeout(Genode::Timer::Microseconds)
void Link::_handle_close_timeout(Duration)
{
dissolve();
_client._interface.link_closed(*this, _protocol);
@ -167,7 +167,7 @@ Tcp_link::Tcp_link(Interface &cln_interface,
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Timer::Connection &timer,
Configuration &config,
uint8_t const protocol)
:
@ -179,7 +179,7 @@ Tcp_link::Tcp_link(Interface &cln_interface,
void Tcp_link::_fin_acked()
{
if (_server_fin_acked && _client_fin_acked) {
_close_timeout.start(_close_timeout_us);
_close_timeout.schedule(_close_timeout_us);
_closed = true;
}
}
@ -228,7 +228,7 @@ Udp_link::Udp_link(Interface &cln_interface,
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Timer::Connection &timer,
Configuration &config,
uint8_t const protocol)
:

View File

@ -15,7 +15,7 @@
#define _LINK_H_
/* Genode includes */
#include <os/timer.h>
#include <timer_session/connection.h>
#include <util/avl_tree.h>
#include <util/list.h>
#include <net/ipv4.h>
@ -129,13 +129,13 @@ class Net::Link : public Link_list::Element
Link_side _client;
Pointer<Port_allocator_guard> const _server_port_alloc;
Link_side _server;
Genode::One_shot_timeout<Link> _close_timeout;
Genode::Timer::Microseconds const _close_timeout_us;
Timer::One_shot_timeout<Link> _close_timeout;
Genode::Microseconds const _close_timeout_us;
Genode::uint8_t const _protocol;
void _handle_close_timeout(Genode::Timer::Microseconds);
void _handle_close_timeout(Genode::Duration);
void _packet() { _close_timeout.start(_close_timeout_us); }
void _packet() { _close_timeout.schedule(_close_timeout_us); }
public:
@ -146,7 +146,7 @@ class Net::Link : public Link_list::Element
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Timer::Connection &timer,
Configuration &config,
Genode::uint8_t const protocol);
@ -188,7 +188,7 @@ class Net::Tcp_link : public Link
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Timer::Connection &timer,
Configuration &config,
Genode::uint8_t const protocol);
@ -205,7 +205,7 @@ struct Net::Udp_link : Link
Pointer<Port_allocator_guard> const srv_port_alloc,
Interface &srv_interface,
Link_side_id const &srv_id,
Genode::Timer &timer,
Timer::Connection &timer,
Configuration &config,
Genode::uint8_t const protocol);

View File

@ -15,7 +15,6 @@
#include <base/component.h>
#include <base/heap.h>
#include <base/attached_rom_dataspace.h>
#include <os/timer.h>
#include <nic/xml_node.h>
#include <timer_session/connection.h>
@ -32,8 +31,7 @@ class Main
{
private:
Timer::Connection _timer_connection;
Genode::Timer _timer;
Timer::Connection _timer;
Genode::Heap _heap;
Genode::Attached_rom_dataspace _config_rom;
Configuration _config;
@ -48,12 +46,10 @@ class Main
Main::Main(Env &env)
:
_timer_connection(env), _timer(_timer_connection, env.ep()),
_heap(&env.ram(), &env.rm()), _config_rom(env, "config"),
_config(_config_rom.xml(), _heap),
_uplink(env, _timer, _heap, _config),
_root(env.ep(), _timer, _heap, _uplink.router_mac(), _config, env.ram(),
env.rm())
_timer(env), _heap(&env.ram(), &env.rm()), _config_rom(env, "config"),
_config(_config_rom.xml(), _heap), _uplink(env, _timer, _heap, _config),
_root(env.ep(), _timer, _heap, _uplink.router_mac(), _config,
env.ram(), env.rm())
{
env.parent().announce(env.ep().manage(_root));
}

View File

@ -1,6 +1,6 @@
TARGET = nic_router
LIBS += base net config timeout
LIBS += base net config
SRC_CC += arp_waiter.cc ip_rule.cc
SRC_CC += component.cc port_allocator.cc forward_rule.cc

View File

@ -24,7 +24,7 @@ using namespace Genode;
Net::Uplink::Uplink(Env &env,
Genode::Timer &timer,
Timer::Connection &timer,
Genode::Allocator &alloc,
Configuration &config)
:

View File

@ -47,7 +47,7 @@ class Net::Uplink : public Nic::Packet_allocator,
public:
Uplink(Genode::Env &env,
Genode::Timer &timer,
Timer::Connection &timer,
Genode::Allocator &alloc,
Configuration &config);

View File

@ -13,42 +13,553 @@
/* Genode includes */
#include <base/component.h>
#include <base/attached_ram_dataspace.h>
#include <timer_session/connection.h>
#include <os/timer.h>
#include <util/fifo.h>
#include <util/misc_math.h>
using namespace Genode;
class Main
struct Test
{
private:
Env &env;
unsigned &error_cnt;
Signal_transmitter done;
unsigned id;
Timer::Connection timer_connection { env };
Timer::Connection timer { env };
using Microseconds = Genode::Timer::Microseconds;
Test(Env &env,
unsigned &error_cnt,
Signal_context_capability done,
unsigned id,
char const *brief)
:
env(env), error_cnt(error_cnt), done(done), id(id)
{
/*
* FIXME Activate interpolation early to give it some time to
* calibrate. Otherwise, we may get non-representative
* results in at least the fast-polling test, which starts
* directly with the heaviest load. This is only necessary
* because Timer::Connection by now must be backwards compatible
* and therefore starts interpolation only on demand.
*/
timer.curr_time();
void _handle(Microseconds now, Cstring name) {
log(now.value / 1000, " ms: ", name, " timeout triggered"); }
log("\nTEST ", id, ": ", brief, "\n");
}
void _handle_pt1(Microseconds now) { _handle(now, "Periodic 700ms"); }
void _handle_pt2(Microseconds now) { _handle(now, "Periodic 1000ms"); }
void _handle_ot1(Microseconds now) { _handle(now, "One-shot 3250ms"); }
void _handle_ot2(Microseconds now) { _handle(now, "One-shot 5200ms"); }
float percentage(unsigned long value, unsigned long base)
{
/*
* FIXME When base = 0 and value != 0, we normally want to return
* FLT_MAX but this causes a compile error. Thus, we use a
* pretty high value instead.
*/
return base ? ((float)value / base * 100) : value ? 1000000 : 0;
}
Timer::Connection _timer_connection;
Genode::Timer _timer;
Periodic_timeout<Main> _pt1 { _timer, *this, &Main::_handle_pt1, Microseconds(700000) };
Periodic_timeout<Main> _pt2 { _timer, *this, &Main::_handle_pt2, Microseconds(1000000) };
One_shot_timeout<Main> _ot1 { _timer, *this, &Main::_handle_ot1 };
One_shot_timeout<Main> _ot2 { _timer, *this, &Main::_handle_ot2 };
~Test() { log("\nTEST ", id, " finished\n"); }
};
public:
Main(Env &env) : _timer_connection(env),
_timer(_timer_connection, env.ep())
{
_ot1.start(Microseconds(3250000));
_ot2.start(Microseconds(5300000));
struct Mixed_timeouts : Test
{
static constexpr char const *brief = "schedule multiple timeouts simultaneously";
enum { NR_OF_EVENTS = 20 };
enum { NR_OF_TIMEOUTS = 4 };
enum { MAX_ERROR_PC = 10 };
struct Timeout
{
char const *const name;
Microseconds const us;
};
struct Event
{
Timeout const *const timeout;
Duration const time;
};
Timeout const timeouts[NR_OF_TIMEOUTS] {
{ "Periodic 700 ms", Microseconds( 700000) },
{ "Periodic 1000 ms", Microseconds(1000000) },
{ "One-shot 3250 ms", Microseconds(3250000) },
{ "One-shot 5200 ms", Microseconds(5200000) }
};
/*
* We want to check only timeouts that have a distance of at least
* 200ms to each other timeout. Thus, the items in this array that
* have an empty name are treated as wildcards and match any timeout.
*/
Event const events[NR_OF_EVENTS] {
{ nullptr, Duration(Milliseconds(0)) },
{ nullptr, Duration(Milliseconds(0)) },
{ &timeouts[0], Duration(Milliseconds(700)) },
{ &timeouts[1], Duration(Milliseconds(1000)) },
{ &timeouts[0], Duration(Milliseconds(1400)) },
{ nullptr, Duration(Milliseconds(2000)) },
{ nullptr, Duration(Milliseconds(2100)) },
{ &timeouts[0], Duration(Milliseconds(2800)) },
{ &timeouts[1], Duration(Milliseconds(3000)) },
{ &timeouts[2], Duration(Milliseconds(3250)) },
{ &timeouts[0], Duration(Milliseconds(3500)) },
{ &timeouts[1], Duration(Milliseconds(4000)) },
{ &timeouts[0], Duration(Milliseconds(4200)) },
{ nullptr, Duration(Milliseconds(4900)) },
{ nullptr, Duration(Milliseconds(5000)) },
{ &timeouts[3], Duration(Milliseconds(5200)) },
{ &timeouts[0], Duration(Milliseconds(5600)) },
{ &timeouts[1], Duration(Milliseconds(6000)) },
{ &timeouts[0], Duration(Milliseconds(6300)) },
{ &timeouts[2], Duration(Milliseconds(6500)) }
};
Duration init_time { Microseconds(0) };
unsigned event_id { 0 };
Timer::Periodic_timeout<Mixed_timeouts> pt1 { timer, *this, &Mixed_timeouts::handle_pt1, timeouts[0].us };
Timer::Periodic_timeout<Mixed_timeouts> pt2 { timer, *this, &Mixed_timeouts::handle_pt2, timeouts[1].us };
Timer::One_shot_timeout<Mixed_timeouts> ot1 { timer, *this, &Mixed_timeouts::handle_ot1 };
Timer::One_shot_timeout<Mixed_timeouts> ot2 { timer, *this, &Mixed_timeouts::handle_ot2 };
void handle_pt1(Duration time) { handle(time, timeouts[0]); }
void handle_pt2(Duration time) { handle(time, timeouts[1]); }
void handle_ot1(Duration time) { handle(time, timeouts[2]); ot1.schedule(timeouts[2].us); }
void handle_ot2(Duration time) { handle(time, timeouts[3]); }
void handle(Duration time, Timeout const &timeout)
{
if (event_id == NR_OF_EVENTS) {
return; }
if (!event_id) {
init_time = time; }
Event const &event = events[event_id++];
unsigned long time_us = time.trunc_to_plain_us().value -
init_time.trunc_to_plain_us().value;
unsigned long event_time_us = event.time.trunc_to_plain_us().value;
unsigned long error_us = max(time_us, event_time_us) -
min(time_us, event_time_us);
float const error_pc = percentage(error_us, timeout.us.value);
log(time_us / 1000UL, " ms: ", timeout.name, " timeout triggered,"
" error ", error_us, " us (", error_pc, " %)");
if (error_pc > MAX_ERROR_PC) {
error("absolute timeout error greater than ", (unsigned)MAX_ERROR_PC, " %");
error_cnt++;
}
if (event.timeout && event.timeout != &timeout) {
error("expected timeout ", timeout.name);
error_cnt++;
}
if (event_id == NR_OF_EVENTS) {
done.submit(); }
}
Mixed_timeouts(Env &env,
unsigned &error_cnt,
Signal_context_capability done,
unsigned id)
:
Test(env, error_cnt, done, id, brief)
{
ot1.schedule(timeouts[2].us);
ot2.schedule(timeouts[3].us);
}
};
struct Fast_polling : Test
{
static constexpr char const *brief = "poll time pretty fast";
enum { NR_OF_ROUNDS = 4 };
enum { MIN_ROUND_DURATION_MS = 2000 };
enum { MAX_NR_OF_POLLS = 10000000 };
enum { MIN_NR_OF_POLLS = 1000 };
enum { STACK_SIZE = 4 * 1024 * sizeof(addr_t) };
enum { MIN_TIME_COMPARISONS = 100 };
enum { MAX_TIME_ERR_US = 10000 };
enum { MAX_AVG_TIME_ERR_US = 1000 };
enum { MAX_DELAY_ERR_US = 2000 };
enum { MAX_AVG_DELAY_ERR_US = 20 };
enum { MAX_POLL_LATENCY_US = 1000 };
enum { BUF_SIZE = MAX_NR_OF_POLLS * sizeof(unsigned long) };
Entrypoint main_ep;
Signal_handler<Fast_polling> main_handler;
Attached_ram_dataspace local_us_buf_1 { env.ram(), env.rm(), BUF_SIZE };
Attached_ram_dataspace local_us_buf_2 { env.ram(), env.rm(), BUF_SIZE };
Attached_ram_dataspace remote_ms_buf { env.ram(), env.rm(), BUF_SIZE };
unsigned long volatile *local_us_1 { local_us_buf_1.local_addr<unsigned long>() };
unsigned long volatile *local_us_2 { local_us_buf_2.local_addr<unsigned long>() };
unsigned long volatile *remote_ms { remote_ms_buf.local_addr<unsigned long>() };
unsigned const delay_loops_per_poll[NR_OF_ROUNDS] { 1,
1000,
10000,
100000 };
/*
* Accumulates great amounts of integer values to one average value
*
* Aims for best possible precision with a fixed amount of integer buffers
*/
struct Average_accumulator
{
private:
unsigned long _avg { 0 };
unsigned long _avg_cnt { 0 };
unsigned long _acc { 0 };
unsigned long _acc_cnt { 0 };
public:
void flush()
{
unsigned long acc_avg = _acc / _acc_cnt;
if (!_avg_cnt) { _avg = _avg + acc_avg; }
else {
float acc_fac = (float)_acc_cnt / _avg_cnt;
_avg = (_avg + ((float)acc_fac * acc_avg)) / (1 + acc_fac);
}
_avg_cnt += _acc_cnt;
_acc = 0;
_acc_cnt = 0;
}
void add(unsigned long add)
{
if (add > (~0UL - _acc)) {
flush(); }
_acc += add;
_acc_cnt++;
}
unsigned long avg()
{
if (_acc_cnt) {
flush(); }
return _avg;
}
unsigned long avg_cnt()
{
if (_acc_cnt) {
flush(); }
return _avg_cnt;
}
};
unsigned long delay_us(unsigned poll)
{
return local_us_1[poll - 1] > local_us_1[poll] ?
local_us_1[poll - 1] - local_us_1[poll] :
local_us_1[poll] - local_us_1[poll - 1];
}
unsigned long estimate_delay_loops_per_ms()
{
log("estimate CPU speed ...");
for (unsigned long max_cnt = 1000UL * 1000UL; ; max_cnt *= 2) {
/* measure consumed time of a limited busy loop */
unsigned long volatile start_ms = timer_connection.elapsed_ms();
for (unsigned long volatile cnt = 0; cnt < max_cnt; cnt++) { }
unsigned long volatile end_ms = timer_connection.elapsed_ms();
/*
* We only return the result if the loop was time intensive enough
* and therefore representative. Otherwise we raise the loop-
* counter limit and do a new estimation.
*/
unsigned long diff_ms = end_ms - start_ms;
if (diff_ms > 1000UL) {
return max_cnt / diff_ms; }
}
}
void main()
{
/*
* Estimate CPU speed
*
* The test delays must be done through busy spinning. If we would
* use a timer session instead, we could not produce delays of only a
* few microseconds. Thus, to get similar delays on each platform we
* have to do this estimation.
*/
unsigned long volatile delay_loops_per_remote_poll =
estimate_delay_loops_per_ms() / 100;
/* do several rounds of the test with different parameters each */
for (unsigned round = 0; round < NR_OF_ROUNDS; round++) {
/* print round parameters */
log("");
log("--- Round ", round + 1,
": polling delay ", delay_loops_per_poll[round], " loop(s) ---");
log("");
unsigned long volatile delay_loops = 0;
unsigned long nr_of_polls = MAX_NR_OF_POLLS;
unsigned long delay_loops_per_poll_ = delay_loops_per_poll[round];
unsigned long end_remote_ms = timer_connection.elapsed_ms() +
MIN_ROUND_DURATION_MS;
/* limit polling to our buffer capacity */
for (unsigned poll = 0; poll < nr_of_polls; poll++) {
/* create delay between two polls */
for (unsigned long volatile i = 0; i < delay_loops_per_poll_; i++) { }
/* count delay loops to limit frequency of remote time reading */
delay_loops += delay_loops_per_poll_;
/*
* We buffer the results in local variables first so the RAM
* access wont raise the delay between the reading of the
* different time values.
*/
unsigned long volatile local_us_1_;
unsigned long volatile local_us_2_;
unsigned long volatile remote_ms_;
/* read local time before the remote time reading */
local_us_1_ = timer.curr_time().trunc_to_plain_us().value;
/*
* Limit frequency of remote-time reading
*
* If we would stress the timer connection to much, the
* back-end functionality of the timeout framework would
* remarkably slow down which causes a phase of adaption with
* bigger errors. But the goal of the framework is to spare
* calls to timer connections anyway. So, its fine to limit
* the polling frequency here.
*/
if (delay_loops > delay_loops_per_remote_poll) {
/* read remote time and second local time */
remote_ms_ = timer_connection.elapsed_ms();
local_us_2_ = timer.curr_time().trunc_to_plain_us().value;
/* reset delay counter for remote-time reading */
delay_loops = 0;
} else {
/* mark remote-time and second local-time value invalid */
remote_ms_ = 0;
local_us_2_ = 0;
}
/* store results to the buffers */
remote_ms[poll] = remote_ms_;
local_us_1[poll] = local_us_1_;
local_us_2[poll] = local_us_2_;
/* if the minimum round duration is reached, end polling */
if (remote_ms_ > end_remote_ms) {
nr_of_polls = poll + 1;
break;
}
}
/*
* Mark results with a bad latency dismissed
*
* It might be, that we got scheduled away between reading out
* local and remote time. This would cause the test result to
* be much worse than the actual precision of the timeout
* framework. Thus, we ignore such results.
*/
unsigned nr_of_good_polls = 0;
unsigned nr_of_bad_polls = 0;
for (unsigned poll = 0; poll < nr_of_polls; poll++) {
if (remote_ms[poll] &&
local_us_2[poll] - local_us_1[poll] > MAX_POLL_LATENCY_US)
{
local_us_1[poll] = 0;
nr_of_bad_polls++;
} else {
nr_of_good_polls++; }
}
/*
* Calculate the average delay between consecutive polls
* (using the local time).
*/
Average_accumulator avg_delay_us;
for (unsigned poll = 1; poll < nr_of_polls; poll++) {
/* skip if this result was dismissed */
if (!local_us_1[poll]) {
poll++;
continue;
}
/* check if local time is monotone */
if (local_us_1[poll - 1] > local_us_1[poll]) {
error("time is not monotone at poll #", poll);
error_cnt++;
}
/* check out delay between this poll and the last one */
avg_delay_us.add(delay_us(poll));
}
/*
* Calculate the average and maximum error of the local time
* compared to the remote time as well as the duration of the
* whole test round.
*/
Average_accumulator avg_time_err_us;
unsigned long max_time_err_us = 0UL;
for (unsigned poll = 0; poll < nr_of_polls; poll++) {
/* skip if this result was dismissed */
if (!local_us_1[poll]) {
continue; }
/* skip if this poll contains no remote time */
if (!remote_ms[poll]) {
continue; }
/* scale-up remote time to microseconds and calculate error */
if (remote_ms[poll] > ~0UL / 1000UL) {
error("can not translate remote time to microseconds");
error_cnt++;
}
unsigned long const remote_us = remote_ms[poll] * 1000UL;
unsigned long const time_err_us = remote_us > local_us_1[poll] ?
remote_us - local_us_1[poll] :
local_us_1[poll] - remote_us;
/* update max time error */
if (time_err_us > max_time_err_us) {
max_time_err_us = time_err_us; }
/* update average time error */
avg_time_err_us.add(time_err_us);
}
Average_accumulator avg_delay_err_us;
unsigned long avg_delay_us_ = avg_delay_us.avg();
/*
* Calculate the average error of the delays compared to the
* average delay (in microseconds and percent of the average
* delay).
*/
unsigned long max_delay_err_us = 0;
for (unsigned poll = 1; poll < nr_of_polls; poll++) {
/* skip if this result was dismissed */
if (!local_us_1[poll]) {
poll++;
continue;
}
unsigned long delay_us_ = delay_us(poll);
unsigned long delay_err_us = delay_us_ > avg_delay_us_ ?
delay_us_ - avg_delay_us_ :
avg_delay_us_ - delay_us_;
if (delay_err_us > max_delay_err_us) {
max_delay_err_us = delay_err_us; }
avg_delay_err_us.add(delay_err_us);
}
unsigned long const max_avg_delay_err_us = (unsigned long)MAX_AVG_DELAY_ERR_US +
avg_delay_us_ / 20;
bool const error_nr_of_good_polls = (nr_of_good_polls < MIN_NR_OF_POLLS);
bool const error_nr_of_time_cmprs = (avg_time_err_us.avg_cnt() < MIN_TIME_COMPARISONS);
bool const error_avg_time_err = (avg_time_err_us.avg() > MAX_AVG_TIME_ERR_US);
bool const error_max_time_err = (max_time_err_us > MAX_TIME_ERR_US);
bool const error_avg_delay_err = (avg_delay_err_us.avg() > max_avg_delay_err_us);
error_cnt += error_nr_of_good_polls;
error_cnt += error_nr_of_time_cmprs;
error_cnt += error_avg_time_err;
error_cnt += error_max_time_err;
error_cnt += error_avg_delay_err;
log(error_nr_of_good_polls ? "\033[31mbad: " : "good: ", "nr of good polls ", nr_of_good_polls, " (min ", (unsigned)MIN_NR_OF_POLLS, ")\033[0m");
log( " ", "nr of bad polls ", nr_of_bad_polls );
log(error_nr_of_time_cmprs ? "\033[31mbad: " : "good: ", "nr of time comparisons ", avg_time_err_us.avg_cnt(), " (min ", (unsigned)MIN_TIME_COMPARISONS, ")\033[0m");
log(error_avg_time_err ? "\033[31mbad: " : "good: ", "average time error ", avg_time_err_us.avg(), " us (max ", (unsigned long)MAX_AVG_TIME_ERR_US, " us)\033[0m");
log(error_max_time_err ? "\033[31mbad: " : "good: ", "maximum time error ", max_time_err_us, " us (max ", (unsigned long)MAX_TIME_ERR_US, " us)\033[0m");
log( " ", "average delay ", avg_delay_us.avg(), " us" );
log(error_avg_delay_err ? "\033[31mbad: " : "good: ", "average delay error ", avg_delay_err_us.avg(), " us (max ", max_avg_delay_err_us, " us)\033[0m");
log( " ", "maximum delay error ", max_delay_err_us, " us" );
}
done.submit();
}
Fast_polling(Env &env,
unsigned &error_cnt,
Signal_context_capability done,
unsigned id)
:
Test(env, error_cnt, done, id, brief),
main_ep(env, STACK_SIZE, "fast_polling_ep"),
main_handler(main_ep, *this, &Fast_polling::main)
{
Signal_transmitter(main_handler).submit();
}
};
struct Main
{
Env &env;
unsigned error_cnt { 0 };
Constructible<Fast_polling> test_1;
Constructible<Mixed_timeouts> test_2;
Signal_handler<Main> test_1_done { env.ep(), *this, &Main::handle_test_1_done };
Signal_handler<Main> test_2_done { env.ep(), *this, &Main::handle_test_2_done };
Main(Env &env) : env(env)
{
test_1.construct(env, error_cnt, test_1_done, 1);
}
void handle_test_1_done()
{
test_1.destruct();
test_2.construct(env, error_cnt, test_2_done, 2);
}
void handle_test_2_done()
{
test_2.destruct();
if (error_cnt) {
error("test failed because of ", error_cnt, " error(s)");
env.parent().exit(-1);
} else {
env.parent().exit(0);
}
}
};

View File

@ -1,4 +1,4 @@
TARGET = test-timeout
SRC_CC += main.cc
LIBS += base timeout
LIBS += base
INC_DIR += $(PRG_DIR)