CMRX provides userspace with timers that can be used to implement low- to medium-resolution event scheduling. Using CMRX timers frees up hardware timers for use with low-latency or high-resolution tasks and avoids the use of ISR handlers to dispatch the interrupt to userspace.
Internally, CMRX kernel is using so-called timing provider to provide all timing facilities to kernel itself. Timing provider is an external kernel-level component that provides timing signals to kernel. That means, CMRX kernel itself does not imply nor force any specific timing source to be used. There is a default timing provider for ARM Cortex-M microcontrollers based on SysTick timer. This timer can be used to quickly bring up CMRX on hardware and can be later replaced by timing provider better suited for any specific use-case, such as battery-powered devices where CPU is shut down completely.
CMRX kernel then multiplexes this timer both for internal and external needs. Internally, timing provider is used to create time quanta and implement preemptive scheduling, while externally the timer provided by timing services is multiplexed into userspace timers. Userspace timers thus inherit the resolution of timing provider used, despite internally, they are specified in nanoseconds.
First kind of timer available for userspace is sleep timer. This timer will suspend thread execution for at least given amount of time. Kernel guarrantees sleep won’t take shorter than requested time, but based on scheduling state at the time of wake up, it may be sleeping longer than requested. Sleep may also be longer than requested if timing provider granularity is close to requested sleep duration.
Very short sleep durations are implemented as busy waits. This implementation was chosen as doing suspend and resume of thread would take comparable amount of time yet would provide less precision. This busy waiting is done with interrupts enabled so any incoming interrupt may prolong tight sleeps.
Thread sleep can be entered via calling the usleep() function.
Any thread can also schedule interval timer. This timer is delivered repeatedly in fixed intervals until cancelled. Currently, the delivery of interval timer is done via SIGALRM signal. Threads are expected to receive the timer by stopping themselves executing send_signal(get_tid(), SIGSTOP) which will stop thread execution until interval timer is delivered. When interval timer is delivered, the thread execution is resumed the same way as if SIGCONG signal was sent to the thread.
When thread is resumed, its execution may still be delayed due to current scheduler state, so depending on priority of thread using interval timer and other threads being ready for execution a jitter may be introduced into timer processing. This jitter doesn’t affect timer interval though.
Interval timers are set up by call to setitimer(). This call will set up interval timer for thread which called this function. Passing 0 as argument will cancel any interval timer for the current thread.
It is possible to combine interval and delay timers in the same thread at the same time.
As said above, CMRX kernel doesn’t contain internal source of clock, rather relies on timing providers. These components are external to the kernel, while still running with kernel privileges. To simplify creation of timing providers, CMRX kernel provides stable API that these provides have to implement. Ultimately the whole purpose of timing provider is to call kernel scheduler timing callback function os_sched_timing_callback() providing kernel with information how much time has passed since last timing provider impulse. Kernel will use timing provider API to set up when these impulses shall be delivered.
This API consists of two functions timing provider has to implement:
timing_provider_schedule will command the timing provider to schedule delivery of timing impulse. Timing provider is normally expected to deliver timing impulse no earlier than after passing specified amount of time. This behavior enables tick-less scheduling. Yet timing providers are free to deliver timing impulses in regular intervals as long as the information on how much time passed since last impulse is correct.timing_provider_delay will command timing provider to perform tight busy wait. This is used to implement tight usleep() sleeps. Timing provider should not disable interrupts during busywaiting.Developers are free to provide their own implementation of this API and link it to CMRX kernel. They can use whatever hardware timing facilities the target platform uses.