diff --git a/os/include/timer_session/timer_session.h b/os/include/timer_session/timer_session.h index 026b445e3..4fa766ba4 100644 --- a/os/include/timer_session/timer_session.h +++ b/os/include/timer_session/timer_session.h @@ -36,6 +36,9 @@ namespace Timer { /** * Program periodic timeout (in microseconds) + * + * The first period will be triggered after 'us' at the latest, + * but it might be triggered earlier as well. */ virtual void trigger_periodic(unsigned us) = 0; diff --git a/os/src/drivers/timer/include/timer_session_component.h b/os/src/drivers/timer/include/timer_session_component.h index 549c0fdbd..130e9ff94 100644 --- a/os/src/drivers/timer/include/timer_session_component.h +++ b/os/src/drivers/timer/include/timer_session_component.h @@ -110,6 +110,7 @@ namespace Timer { void sigh(Signal_context_capability sigh) { _sigh = sigh; } void periodic(bool periodic) { _periodic = periodic; } + bool periodic() { return _periodic; } /********************* @@ -182,10 +183,13 @@ namespace Timer { /** * Called from the '_trigger' function executed by the server activation */ - void schedule_timeout(Genode::Alarm *alarm, Genode::Alarm::Time timeout) + void schedule_timeout(Wake_up_alarm *alarm, Genode::Alarm::Time timeout) { Alarm::Time now = _platform_timer->curr_time(); - schedule_absolute(alarm, now + timeout); + if (alarm->periodic()) { + handle(now); /* update '_now' in 'Alarm_scheduler' */ + schedule(alarm, timeout); + } else schedule_absolute(alarm, now + timeout); /* interrupt current 'wait_for_timeout' */ _platform_timer->schedule_timeout(0); @@ -258,7 +262,7 @@ namespace Timer { unsigned long elapsed_ms() const { unsigned long const now = _timeout_scheduler.curr_time(); - return now - _initial_time; + return (now - _initial_time) / 1000; } void msleep(unsigned) { /* never called at the server side */ } diff --git a/os/src/test/timer/main.cc b/os/src/test/timer/main.cc index ce4a5cbe4..128f5de63 100644 --- a/os/src/test/timer/main.cc +++ b/os/src/test/timer/main.cc @@ -91,6 +91,34 @@ int main(int argc, char **argv) main_timer.msleep(2000); printf("timeout fired\n"); + /* check periodic timeouts */ + Signal_receiver sig_rcv; + Signal_context sig_cxt; + Signal_context_capability sig = sig_rcv.manage(&sig_cxt); + main_timer.sigh(sig); + enum { PTEST_TIME_US = 2000000 }; + unsigned period_us = 500000, periods = PTEST_TIME_US / period_us, i = 0; + printf("start periodic timeouts\n"); + for (unsigned j = 0; j < 5; j++) { + unsigned elapsed_ms = main_timer.elapsed_ms(); + main_timer.trigger_periodic(period_us); + while (i < periods) { + Signal s = sig_rcv.wait_for_signal(); + i += s.num(); + } + elapsed_ms = main_timer.elapsed_ms() - elapsed_ms; + unsigned const min_ms = ((i - 1) * period_us) / 1000; + unsigned const max_ms = (i * period_us) / 1000; + if (min_ms > elapsed_ms || max_ms < elapsed_ms) { + PERR("Timing %u ms period %u times failed: %u ms (min %u, max %u)", + period_us / 1000, i, elapsed_ms, min_ms, max_ms); + return -1; + } + printf("Done %u ms period %u times: %u ms (min %u, max %u)\n", + period_us / 1000, i, elapsed_ms, min_ms, max_ms); + i = 0, period_us /= 2, periods = PTEST_TIME_US / period_us; + } + /* create timer clients with different periods */ for (unsigned period_msec = 1; period_msec < 28; period_msec++) { Timer_client *tc = new (env()->heap()) Timer_client(period_msec);