diff --git a/repos/base-fiasco/lib/mk/base-fiasco.mk b/repos/base-fiasco/lib/mk/base-fiasco.mk index dfca0008e..6a594d41a 100644 --- a/repos/base-fiasco/lib/mk/base-fiasco.mk +++ b/repos/base-fiasco/lib/mk/base-fiasco.mk @@ -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 diff --git a/repos/base-foc/lib/mk/base-foc.inc b/repos/base-foc/lib/mk/base-foc.inc index 37538f73f..3ad06fce1 100644 --- a/repos/base-foc/lib/mk/base-foc.inc +++ b/repos/base-foc/lib/mk/base-foc.inc @@ -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 diff --git a/repos/base-hw/include/kernel/interface.h b/repos/base-hw/include/kernel/interface.h index dcd8befcb..a99db6997 100644 --- a/repos/base-hw/include/kernel/interface.h +++ b/repos/base-hw/include/kernel/interface.h @@ -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 * diff --git a/repos/base-hw/lib/mk/base-hw.mk b/repos/base-hw/lib/mk/base-hw.mk index dae3cbdfc..c861eadcd 100644 --- a/repos/base-hw/lib/mk/base-hw.mk +++ b/repos/base-hw/lib/mk/base-hw.mk @@ -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 diff --git a/repos/base-hw/lib/mk/timeout-hw.mk b/repos/base-hw/lib/mk/timeout-hw.mk new file mode 100644 index 000000000..235f50ec7 --- /dev/null +++ b/repos/base-hw/lib/mk/timeout-hw.mk @@ -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 diff --git a/repos/base-hw/src/core/kernel/cpu.cc b/repos/base-hw/src/core/kernel/cpu.cc index f66bfd43b..6efe8aba3 100644 --- a/repos/base-hw/src/core/kernel/cpu.cc +++ b/repos/base-hw/src/core/kernel/cpu.cc @@ -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(); diff --git a/repos/base-hw/src/core/kernel/cpu.h b/repos/base-hw/src/core/kernel/cpu.h index e81f78c63..5f052217e 100644 --- a/repos/base-hw/src/core/kernel/cpu.h +++ b/repos/base-hw/src/core/kernel/cpu.h @@ -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 ** ***************/ diff --git a/repos/base-hw/src/core/kernel/thread.cc b/repos/base-hw/src/core/kernel/thread.cc index 1fde96c25..0cc20312b 100644 --- a/repos/base-hw/src/core/kernel/thread.cc +++ b/repos/base-hw/src/core/kernel/thread.cc @@ -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()) { diff --git a/repos/base-hw/src/core/kernel/timer.h b/repos/base-hw/src/core/kernel/timer.h index d3c0e45c8..f51c5aeac 100644 --- a/repos/base-hw/src/core/kernel/timer.h +++ b/repos/base-hw/src/core/kernel/timer.h @@ -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_ */ diff --git a/repos/base-linux/lib/mk/base-linux.mk b/repos/base-linux/lib/mk/base-linux.mk index f020b24ae..767921c27 100644 --- a/repos/base-linux/lib/mk/base-linux.mk +++ b/repos/base-linux/lib/mk/base-linux.mk @@ -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 diff --git a/repos/base-linux/lib/mk/lx_hybrid.mk b/repos/base-linux/lib/mk/lx_hybrid.mk index 2ffba5194..5e2907de7 100644 --- a/repos/base-linux/lib/mk/lx_hybrid.mk +++ b/repos/base-linux/lib/mk/lx_hybrid.mk @@ -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 diff --git a/repos/base-nova/lib/mk/base-nova.mk b/repos/base-nova/lib/mk/base-nova.mk index d8ebb9f4d..45aa1d988 100644 --- a/repos/base-nova/lib/mk/base-nova.mk +++ b/repos/base-nova/lib/mk/base-nova.mk @@ -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 diff --git a/repos/base-okl4/lib/mk/base-okl4.mk b/repos/base-okl4/lib/mk/base-okl4.mk index b2b05fccb..4d6223e9c 100644 --- a/repos/base-okl4/lib/mk/base-okl4.mk +++ b/repos/base-okl4/lib/mk/base-okl4.mk @@ -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 diff --git a/repos/base-pistachio/lib/mk/base-pistachio.mk b/repos/base-pistachio/lib/mk/base-pistachio.mk index 7ceb8bd9e..37a55542d 100644 --- a/repos/base-pistachio/lib/mk/base-pistachio.mk +++ b/repos/base-pistachio/lib/mk/base-pistachio.mk @@ -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 diff --git a/repos/base-sel4/lib/mk/base-sel4.mk b/repos/base-sel4/lib/mk/base-sel4.mk index e9faa7f35..3ebad650f 100644 --- a/repos/base-sel4/lib/mk/base-sel4.mk +++ b/repos/base-sel4/lib/mk/base-sel4.mk @@ -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 diff --git a/repos/base/lib/symbols/ld b/repos/base/lib/symbols/ld index 6994b5eab..dcc787218 100644 --- a/repos/base/lib/symbols/ld +++ b/repos/base/lib/symbols/ld @@ -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 diff --git a/repos/dde_linux/src/lib/lxip/lxcc_emul.cc b/repos/dde_linux/src/lib/lxip/lxcc_emul.cc index 87f26e656..416f98bd4 100644 --- a/repos/dde_linux/src/lib/lxip/lxcc_emul.cc +++ b/repos/dde_linux/src/lib/lxip/lxcc_emul.cc @@ -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; } diff --git a/repos/gems/src/server/terminal/main.cc b/repos/gems/src/server/terminal/main.cc index c0dd9de57..f81b9f494 100644 --- a/repos/gems/src/server/terminal/main.cc +++ b/repos/gems/src/server/terminal/main.cc @@ -24,7 +24,6 @@ #include #include #include -#include /* terminal includes */ #include @@ -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
_input_handler { _env.ep(), *this, &Main::_handle_input }; - void _handle_key_repeat(Time_source::Microseconds); + void _handle_key_repeat(Duration); - One_shot_timeout
_key_repeat_timeout { + Timer::One_shot_timeout
_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
_flush_timeout { + Timer::One_shot_timeout
_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(); diff --git a/repos/gems/src/server/terminal/target.mk b/repos/gems/src/server/terminal/target.mk index dba8b32b5..46b6e93be 100644 --- a/repos/gems/src/server/terminal/target.mk +++ b/repos/gems/src/server/terminal/target.mk @@ -1,4 +1,4 @@ TARGET = terminal SRC_CC = main.cc -LIBS = base timeout +LIBS = base SRC_BIN = $(notdir $(wildcard $(PRG_DIR)/*.tff)) diff --git a/repos/libports/lib/mk/libc.mk b/repos/libports/lib/mk/libc.mk index 7eb2cc223..b06f95b15 100644 --- a/repos/libports/lib/mk/libc.mk +++ b/repos/libports/lib/mk/libc.mk @@ -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 diff --git a/repos/libports/src/lib/libc/nanosleep.cc b/repos/libports/src/lib/libc/nanosleep.cc index df6de9e90..9c0738a73 100644 --- a/repos/libports/src/lib/libc/nanosleep.cc +++ b/repos/libports/src/lib/libc/nanosleep.cc @@ -16,8 +16,6 @@ #include "task.h" -using namespace Genode; - extern "C" __attribute__((weak)) int _nanosleep(const struct timespec *req, struct timespec *rem) { diff --git a/repos/libports/src/lib/libc/task.cc b/repos/libports/src/lib/libc/task.cc index 4494fa294..77f366b45 100644 --- a/repos/libports/src/lib/libc/task.cc +++ b/repos/libports/src/lib/libc/task.cc @@ -23,7 +23,6 @@ #include #include #include -#include /* libc includes */ #include @@ -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; + Libc::Timer_accessor &_timer_accessor; + Timeout_handler &_handler; + ::Timer::One_shot_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 diff --git a/repos/libports/src/lib/libc/task.h b/repos/libports/src/lib/libc/task.h index d6f1395ab..00397e283 100644 --- a/repos/libports/src/lib/libc/task.h +++ b/repos/libports/src/lib/libc/task.h @@ -21,10 +21,6 @@ #ifndef _LIBC__TASK_H_ #define _LIBC__TASK_H_ -/* Genode includes */ -#include - - namespace Libc { /** diff --git a/repos/os/include/os/duration.h b/repos/os/include/os/duration.h new file mode 100644 index 000000000..16d94b3d6 --- /dev/null +++ b/repos/os/include/os/duration.h @@ -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 +#include + +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_ */ diff --git a/repos/os/include/os/time_source.h b/repos/os/include/os/time_source.h deleted file mode 100644 index 78d77cf18..000000000 --- a/repos/os/include/os/time_source.h +++ /dev/null @@ -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_ */ diff --git a/repos/os/include/os/timer.h b/repos/os/include/os/timer.h deleted file mode 100644 index 4f6481f2f..000000000 --- a/repos/os/include/os/timer.h +++ /dev/null @@ -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 -#include -#include - -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::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_ */ diff --git a/repos/os/include/os/timeout.h b/repos/os/include/timer/timeout.h similarity index 58% rename from repos/os/include/os/timeout.h rename to repos/os/include/timer/timeout.h index 4fed98d11..ac75445cd 100644 --- a/repos/os/include/os/timeout.h +++ b/repos/os/include/timer/timeout.h @@ -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 -#include #include +#include +#include namespace Genode { + class Time_source; class Timeout_scheduler; class Timeout; class Alarm_timeout_scheduler; - template class Periodic_timeout; - template 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 -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 -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_ */ diff --git a/repos/os/include/timer_session/connection.h b/repos/os/include/timer_session/connection.h index b0839f4e3..c2e4cd6a9 100644 --- a/repos/os/include/timer_session/connection.h +++ b/repos/os/include/timer_session/connection.h @@ -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 #include +#include +#include +#include +#include -namespace Timer { class Connection; } +namespace Timer +{ + class Connection; + template class Periodic_timeout; + template class One_shot_timeout; +} -class Timer::Connection : public Genode::Connection, public Session_client +/** + * Periodic timeout that is linked to a custom handler, scheduled when constructed + */ +template +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 +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, + 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, 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 _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(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, public Session_cli * \deprecated Use the constructor with 'Env &' as first * argument instead */ - Connection() __attribute__((deprecated)) - : - Genode::Connection(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, 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_ */ diff --git a/repos/os/lib/mk/timeout.mk b/repos/os/lib/mk/timeout.mk index 2c489a767..eaddf2777 100644 --- a/repos/os/lib/mk/timeout.mk +++ b/repos/os/lib/mk/timeout.mk @@ -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 diff --git a/repos/os/run/timeout.run b/repos/os/run/timeout.run index 671bcec62..8cf04ca95 100644 --- a/repos/os/run/timeout.run +++ b/repos/os/run/timeout.run @@ -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 { - + } @@ -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} diff --git a/repos/os/src/drivers/timer/fiasco/time_source.cc b/repos/os/src/drivers/timer/fiasco/time_source.cc index 35bb2fc64..91d706675 100644 --- a/repos/os/src/drivers/timer/fiasco/time_source.cc +++ b/repos/os/src/drivers/timer/fiasco/time_source.cc @@ -39,7 +39,8 @@ namespace Fiasco { #include 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(); - return Microseconds(kip->clock); + return Duration(Microseconds(kip->clock)); } diff --git a/repos/os/src/drivers/timer/hw/time_source.cc b/repos/os/src/drivers/timer/hw/time_source.cc index 96c979149..722a2f9dc 100644 --- a/repos/os/src/drivers/timer/hw/time_source.cc +++ b/repos/os/src/drivers/timer/hw/time_source.cc @@ -23,7 +23,6 @@ #include 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; } diff --git a/repos/os/src/drivers/timer/hw/time_source.h b/repos/os/src/drivers/timer/hw/time_source.h index 0eb29f165..73e507746 100644 --- a/repos/os/src/drivers/timer/hw/time_source.h +++ b/repos/os/src/drivers/timer/hw/time_source.h @@ -14,17 +14,26 @@ #ifndef _TIME_SOURCE_H_ #define _TIME_SOURCE_H_ +/* Genode includes */ +#include + /* local includes */ #include -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); }; diff --git a/repos/os/src/drivers/timer/include/root_component.h b/repos/os/src/drivers/timer/include/root_component.h index b123898ed..41045b0b7 100644 --- a/repos/os/src/drivers/timer/include/root_component.h +++ b/repos/os/src/drivers/timer/include/root_component.h @@ -56,7 +56,9 @@ class Timer::Root_component : public Genode::Root_component : Genode::Root_component(&env.ep().rpc_ep(), &md_alloc), _time_source(env), _timeout_scheduler(_time_source) - { } + { + _timeout_scheduler._enable(); + } }; #endif /* _ROOT_COMPONENT_H_ */ diff --git a/repos/os/src/drivers/timer/include/session_component.h b/repos/os/src/drivers/timer/include/session_component.h index 61ec05f44..09f1e8e2d 100644 --- a/repos/os/src/drivers/timer/include/session_component.h +++ b/repos/os/src/drivers/timer/include/session_component.h @@ -21,9 +21,14 @@ #include #include #include -#include +#include -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, @@ -36,9 +41,10 @@ class Timer::Session_component : public Genode::Rpc_object, 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, } 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 */ } diff --git a/repos/os/src/drivers/timer/include/signalled_time_source.h b/repos/os/src/drivers/timer/include/signalled_time_source.h index 9a9b18888..2e7c30d05 100644 --- a/repos/os/src/drivers/timer/include/signalled_time_source.h +++ b/repos/os/src/drivers/timer/include/signalled_time_source.h @@ -16,7 +16,7 @@ /* Genode includes */ #include -#include +#include #include namespace Genode { class Signalled_time_source; } diff --git a/repos/os/src/drivers/timer/include/threaded_time_source.h b/repos/os/src/drivers/timer/include/threaded_time_source.h index 1f21d5f3f..fa13aaeaf 100644 --- a/repos/os/src/drivers/timer/include/threaded_time_source.h +++ b/repos/os/src/drivers/timer/include/threaded_time_source.h @@ -18,10 +18,12 @@ /* Genode inludes */ #include #include -#include +#include 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; diff --git a/repos/os/src/drivers/timer/linux/time_source.cc b/repos/os/src/drivers/timer/linux/time_source.cc index 0d6937289..ddaaecfb7 100644 --- a/repos/os/src/drivers/timer/linux/time_source.cc +++ b/repos/os/src/drivers/timer/linux/time_source.cc @@ -20,7 +20,6 @@ #include 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)); } diff --git a/repos/os/src/drivers/timer/nova/time_source.cc b/repos/os/src/drivers/timer/nova/time_source.cc index a17fa4f76..a92ba0da1 100644 --- a/repos/os/src/drivers/timer/nova/time_source.cc +++ b/repos/os/src/drivers/timer/nova/time_source.cc @@ -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) diff --git a/repos/os/src/drivers/timer/nova/time_source.h b/repos/os/src/drivers/timer/nova/time_source.h index c775708ba..ad7794bb4 100644 --- a/repos/os/src/drivers/timer/nova/time_source.h +++ b/repos/os/src/drivers/timer/nova/time_source.h @@ -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_ */ diff --git a/repos/os/src/drivers/timer/periodic/time_source.cc b/repos/os/src/drivers/timer/periodic/time_source.cc index c5e81ae2e..0a67971e2 100644 --- a/repos/os/src/drivers/timer/periodic/time_source.cc +++ b/repos/os/src/drivers/timer/periodic/time_source.cc @@ -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; diff --git a/repos/os/src/drivers/timer/periodic/time_source.h b/repos/os/src/drivers/timer/periodic/time_source.h index 98519bd3c..d43a47b8f 100644 --- a/repos/os/src/drivers/timer/periodic/time_source.h +++ b/repos/os/src/drivers/timer/periodic/time_source.h @@ -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; }; diff --git a/repos/os/src/drivers/timer/pit/time_source.cc b/repos/os/src/drivers/timer/pit/time_source.cc index 8c1752f01..737126d10 100644 --- a/repos/os/src/drivers/timer/pit/time_source.cc +++ b/repos/os/src/drivers/timer/pit/time_source.cc @@ -16,7 +16,6 @@ #include 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)); } diff --git a/repos/os/src/drivers/timer/pit/time_source.h b/repos/os/src/drivers/timer/pit/time_source.h index 62e26bdce..0d192162c 100644 --- a/repos/os/src/drivers/timer/pit/time_source.h +++ b/repos/os/src/drivers/timer/pit/time_source.h @@ -18,11 +18,17 @@ /* Genode includes */ #include #include +#include /* local includes */ #include -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); } diff --git a/repos/os/src/drivers/timer/target.inc b/repos/os/src/drivers/timer/target.inc index 72b8f8e65..b110dc04b 100644 --- a/repos/os/src/drivers/timer/target.inc +++ b/repos/os/src/drivers/timer/target.inc @@ -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 diff --git a/repos/os/src/lib/timeout/duration.cc b/repos/os/src/lib/timeout/duration.cc new file mode 100644 index 000000000..41321ebf6 --- /dev/null +++ b/repos/os/src/lib/timeout/duration.cc @@ -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 + +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)); +} diff --git a/repos/os/src/lib/timeout/hw/timer_connection_timestamp.cc b/repos/os/src/lib/timeout/hw/timer_connection_timestamp.cc new file mode 100644 index 000000000..dc2f04b04 --- /dev/null +++ b/repos/os/src/lib/timeout/hw/timer_connection_timestamp.cc @@ -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 +#include + +using namespace Genode; + + +Trace::Timestamp Timer::Connection::_timestamp() +{ + return Kernel::time(); +} diff --git a/repos/os/src/lib/timeout/timeout.cc b/repos/os/src/lib/timeout/timeout.cc index b5bfd532d..4846f7a0c 100644 --- a/repos/os/src/lib/timeout/timeout.cc +++ b/repos/os/src/lib/timeout/timeout.cc @@ -12,7 +12,7 @@ */ /* Genode includes */ -#include +#include 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); } diff --git a/repos/os/src/lib/timeout/timer_connection.cc b/repos/os/src/lib/timeout/timer_connection.cc new file mode 100644 index 000000000..3a3f33a96 --- /dev/null +++ b/repos/os/src/lib/timeout/timer_connection.cc @@ -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 +#include + +using namespace Genode; +using namespace Genode::Trace; + + +void Timer::Connection::_update_interpolation_quality(unsigned long min_factor, + unsigned long max_factor) +{ + /* + * If the factor gets adapted less than 12.5%, we raise the + * interpolation-quality value. Otherwise, we reset it to zero. + * 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_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(_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(env, session(env.parent(), + "ram_quota=10K, cap_quota=%u, label=\"%s\"", + CAP_QUOTA, label)), + Session_client(cap()), + _signal_handler(env.ep(), *this, &Connection::_handle_timeout) +{ + /* register default signal handler */ + Session_client::sigh(_default_sigh_cap); +} + + +Timer::Connection::Connection() +: + Genode::Connection(session("ram_quota=10K")), + Session_client(cap()), + _signal_handler(internal_env().ep(), *this, &Connection::_handle_timeout) +{ + /* register default signal handler */ + Session_client::sigh(_default_sigh_cap); +} + + +void Timer::Connection::_schedule_one_shot(Timeout &timeout, Microseconds duration) +{ + _enable_modern_mode(); + _scheduler._schedule_one_shot(timeout, duration); +}; + + +void Timer::Connection::_schedule_periodic(Timeout &timeout, Microseconds duration) +{ + _enable_modern_mode(); + _scheduler._schedule_periodic(timeout, duration); +}; + + +void Timer::Connection::_discard(Timeout &timeout) +{ + _enable_modern_mode(); + _scheduler._discard(timeout); +} diff --git a/repos/os/src/lib/timeout/timer_connection_timestamp.cc b/repos/os/src/lib/timeout/timer_connection_timestamp.cc new file mode 100644 index 000000000..8e7928235 --- /dev/null +++ b/repos/os/src/lib/timeout/timer_connection_timestamp.cc @@ -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 +#include + +using namespace Genode; + + +Trace::Timestamp Timer::Connection::_timestamp() +{ + return Trace::timestamp(); +} diff --git a/repos/os/src/server/input_filter/chargen_source.h b/repos/os/src/server/input_filter/chargen_source.h index 670132583..a256e141c 100644 --- a/repos/os/src/server/input_filter/chargen_source.h +++ b/repos/os/src/server/input_filter/chargen_source.h @@ -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 _timeout { + Timer::One_shot_timeout _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() diff --git a/repos/os/src/server/input_filter/main.cc b/repos/os/src/server/input_filter/main.cc index f42dc4105..0209f53c8 100644 --- a/repos/os/src/server/input_filter/main.cc +++ b/repos/os/src/server/input_filter/main.cc @@ -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); diff --git a/repos/os/src/server/input_filter/target.mk b/repos/os/src/server/input_filter/target.mk index af45ee015..dc5b5350d 100644 --- a/repos/os/src/server/input_filter/target.mk +++ b/repos/os/src/server/input_filter/target.mk @@ -1,4 +1,4 @@ TARGET = input_filter SRC_CC = main.cc -LIBS = base timeout +LIBS = base INC_DIR += $(PRG_DIR) diff --git a/repos/os/src/server/input_filter/timer_accessor.h b/repos/os/src/server/input_filter/timer_accessor.h index dfecc45a3..7bfe0bef3 100644 --- a/repos/os/src/server/input_filter/timer_accessor.h +++ b/repos/os/src/server/input_filter/timer_accessor.h @@ -15,10 +15,10 @@ #define _INPUT_FILTER__TIMER_ACCESSOR_H_ /* Genode includes */ -#include +#include 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_ */ diff --git a/repos/os/src/server/nic_dump/component.cc b/repos/os/src/server/nic_dump/component.cc index 4a6af43e2..fc67efce5 100644 --- a/repos/os/src/server/nic_dump/component.cc +++ b/repos/os/src/server/nic_dump/component.cc @@ -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 ®ion_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 ®ion_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 ®ion_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 ®ion_map) : Root_component(&ep.rpc_ep(), &alloc), diff --git a/repos/os/src/server/nic_dump/component.h b/repos/os/src/server/nic_dump/component.h index 9e08745c3..1ebbcab1e 100644 --- a/repos/os/src/server/nic_dump/component.h +++ b/repos/os/src/server/nic_dump/component.h @@ -93,7 +93,7 @@ class Net::Session_component : public Session_component_base, Genode::Region_map ®ion_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 #include -#include +#include namespace Net { @@ -49,7 +49,7 @@ class Net::Interface Genode::Allocator &_alloc; Pointer _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); diff --git a/repos/os/src/server/nic_dump/main.cc b/repos/os/src/server/nic_dump/main.cc index 193e25706..f225aa07b 100644 --- a/repos/os/src/server/nic_dump/main.cc +++ b/repos/os/src/server/nic_dump/main.cc @@ -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()) diff --git a/repos/os/src/server/nic_dump/target.mk b/repos/os/src/server/nic_dump/target.mk index a8e7ae9f8..7737008c7 100644 --- a/repos/os/src/server/nic_dump/target.mk +++ b/repos/os/src/server/nic_dump/target.mk @@ -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 diff --git a/repos/os/src/server/nic_dump/uplink.cc b/repos/os/src/server/nic_dump/uplink.cc index 2d92f669b..82f124c88 100644 --- a/repos/os/src/server/nic_dump/uplink.cc +++ b/repos/os/src/server/nic_dump/uplink.cc @@ -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), diff --git a/repos/os/src/server/nic_dump/uplink.h b/repos/os/src/server/nic_dump/uplink.h index ec6687057..b81339e8f 100644 --- a/repos/os/src/server/nic_dump/uplink.h +++ b/repos/os/src/server/nic_dump/uplink.h @@ -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); }; diff --git a/repos/os/src/server/nic_router/component.cc b/repos/os/src/server/nic_router/component.cc index a8bd64c87..3ab9ec0ac 100644 --- a/repos/os/src/server/nic_router/component.cc +++ b/repos/os/src/server/nic_router/component.cc @@ -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, diff --git a/repos/os/src/server/nic_router/component.h b/repos/os/src/server/nic_router/component.h index ba2c03117..f47dbfda3 100644 --- a/repos/os/src/server/nic_router/component.h +++ b/repos/os/src/server/nic_router/component.h @@ -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 { 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 public: Root(Genode::Entrypoint &ep, - Genode::Timer &timer, + Timer::Connection &timer, Genode::Allocator &alloc, Mac_address const &router_mac, Configuration &config, diff --git a/repos/os/src/server/nic_router/interface.cc b/repos/os/src/server/nic_router/interface.cc index 16fb4446c..e45a81264 100644 --- a/repos/os/src/server/nic_router/interface.cc +++ b/repos/os/src/server/nic_router/interface.cc @@ -570,7 +570,7 @@ void Interface::_send(Ethernet_frame ð, 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, diff --git a/repos/os/src/server/nic_router/interface.h b/repos/os/src/server/nic_router/interface.h index 3898e9a2d..683416304 100644 --- a/repos/os/src/server/nic_router/interface.h +++ b/repos/os/src/server/nic_router/interface.h @@ -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, diff --git a/repos/os/src/server/nic_router/link.cc b/repos/os/src/server/nic_router/link.cc index f4cb023d4..a1ec89425 100644 --- a/repos/os/src/server/nic_router/link.cc +++ b/repos/os/src/server/nic_router/link.cc @@ -115,7 +115,7 @@ Link::Link(Interface &cln_interface, Pointer 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 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 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) : diff --git a/repos/os/src/server/nic_router/link.h b/repos/os/src/server/nic_router/link.h index 44492722f..56f5c5e2d 100644 --- a/repos/os/src/server/nic_router/link.h +++ b/repos/os/src/server/nic_router/link.h @@ -15,7 +15,7 @@ #define _LINK_H_ /* Genode includes */ -#include +#include #include #include #include @@ -129,13 +129,13 @@ class Net::Link : public Link_list::Element Link_side _client; Pointer const _server_port_alloc; Link_side _server; - Genode::One_shot_timeout _close_timeout; - Genode::Timer::Microseconds const _close_timeout_us; + Timer::One_shot_timeout _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 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 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 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); diff --git a/repos/os/src/server/nic_router/main.cc b/repos/os/src/server/nic_router/main.cc index 3c2720434..76de05c23 100644 --- a/repos/os/src/server/nic_router/main.cc +++ b/repos/os/src/server/nic_router/main.cc @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -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)); } diff --git a/repos/os/src/server/nic_router/target.mk b/repos/os/src/server/nic_router/target.mk index aa1e192c8..3404d8d9a 100644 --- a/repos/os/src/server/nic_router/target.mk +++ b/repos/os/src/server/nic_router/target.mk @@ -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 diff --git a/repos/os/src/server/nic_router/uplink.cc b/repos/os/src/server/nic_router/uplink.cc index d2d5bdf99..61de2afc4 100644 --- a/repos/os/src/server/nic_router/uplink.cc +++ b/repos/os/src/server/nic_router/uplink.cc @@ -24,7 +24,7 @@ using namespace Genode; Net::Uplink::Uplink(Env &env, - Genode::Timer &timer, + Timer::Connection &timer, Genode::Allocator &alloc, Configuration &config) : diff --git a/repos/os/src/server/nic_router/uplink.h b/repos/os/src/server/nic_router/uplink.h index d6358c4a1..0d6165fdc 100644 --- a/repos/os/src/server/nic_router/uplink.h +++ b/repos/os/src/server/nic_router/uplink.h @@ -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); diff --git a/repos/os/src/test/timeout/main.cc b/repos/os/src/test/timeout/main.cc index 107f8d316..d9c79be82 100644 --- a/repos/os/src/test/timeout/main.cc +++ b/repos/os/src/test/timeout/main.cc @@ -13,42 +13,553 @@ /* Genode includes */ #include +#include #include -#include - +#include +#include 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
_pt1 { _timer, *this, &Main::_handle_pt1, Microseconds(700000) }; - Periodic_timeout
_pt2 { _timer, *this, &Main::_handle_pt2, Microseconds(1000000) }; - One_shot_timeout
_ot1 { _timer, *this, &Main::_handle_ot1 }; - One_shot_timeout
_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 pt1 { timer, *this, &Mixed_timeouts::handle_pt1, timeouts[0].us }; + Timer::Periodic_timeout pt2 { timer, *this, &Mixed_timeouts::handle_pt2, timeouts[1].us }; + Timer::One_shot_timeout ot1 { timer, *this, &Mixed_timeouts::handle_ot1 }; + Timer::One_shot_timeout 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 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 volatile *local_us_2 { local_us_buf_2.local_addr() }; + unsigned long volatile *remote_ms { remote_ms_buf.local_addr() }; + + 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 test_1; + Constructible test_2; + Signal_handler
test_1_done { env.ep(), *this, &Main::handle_test_1_done }; + Signal_handler
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); + } + } }; diff --git a/repos/os/src/test/timeout/target.mk b/repos/os/src/test/timeout/target.mk index 146f03935..d5e522101 100644 --- a/repos/os/src/test/timeout/target.mk +++ b/repos/os/src/test/timeout/target.mk @@ -1,4 +1,4 @@ TARGET = test-timeout SRC_CC += main.cc -LIBS += base timeout +LIBS += base INC_DIR += $(PRG_DIR)