pub trait SchedulerTimer {
// Required methods
fn start(&self, us: NonZeroU32);
fn reset(&self);
fn arm(&self);
fn disarm(&self);
fn get_remaining_us(&self) -> Option<NonZeroU32>;
}
Expand description
Interface for the system scheduler timer.
A system scheduler timer provides a countdown timer to enforce process scheduling time quanta. Implementations should have consistent timing while the CPU is active, but need not operate during sleep. Note, many scheduler implementations also charge time spent running the kernel on behalf of the process against the process time quantum.
The primary requirement an implementation of this interface must satisfy is it must be capable of generating an interrupt when the timer expires. This interrupt will interrupt the executing process, returning control to the kernel, and allowing the scheduler to make decisions about what to run next.
On most chips, this interface will be implemented by a core peripheral (e.g. the ARM core SysTick peripheral). However, some chips lack this optional peripheral, in which case it might be implemented by another timer or alarm peripheral, or require virtualization on top of a shared hardware timer.
The SchedulerTimer
interface is carefully designed to be rather general to
support the various implementations required on different hardware
platforms. The general operation is the kernel will start a timer, which
starts the time quantum assigned to a process. While the process is running,
the kernel will arm the timer, telling the implementation it must ensure
that an interrupt will occur when the time quantum is exhausted. When the
process has stopped running, the kernel will disarm the timer, indicating to
the implementation that an interrupt is no longer required. To check if the
process has exhausted its time quantum the kernel will explicitly ask the
implementation. The kernel itself does not expect to get an interrupt to
handle when the time quantum is exhausted. This is because the time quantum
may end while the kernel itself is running, and the kernel does not need to
effectively preempt itself.
The arm()
and disarm()
functions in this interface serve as an optional
optimization opportunity. This pair allows an implementation to only enable
the interrupt when it is strictly necessary, i.e. while the process is
actually executing. However, a correct implementation can have interrupts
enabled anytime the scheduler timer has been started. What the
implementation must ensure is that the interrupt is enabled when arm()
is
called.
Implementations must take care when using interrupts. Since the
SchedulerTimer
is used in the core kernel loop and scheduler, top half
interrupt handlers may not have executed before SchedulerTimer
functions
are called. In particular, implementations on top of virtualized timers may
receive the interrupt fired upcall “late” (i.e. after the kernel calls
has_expired()
). Implementations should ensure that they can reliably check
for timeslice expirations.
Required Methods§
Sourcefn start(&self, us: NonZeroU32)
fn start(&self, us: NonZeroU32)
Start a timer for a process timeslice. The us
argument is the length
of the timeslice in microseconds.
This must set a timer for an interval as close as possible to the given
interval in microseconds. Interrupts do not need to be enabled. However,
if the implementation cannot separate time keeping from interrupt
generation, the implementation of start()
should enable interrupts and
leave them enabled anytime the timer is active.
Callers can assume at least a 24-bit wide clock. Specific timing is dependent on the driving clock. For ARM boards with a dedicated SysTick peripheral, increments of 10ms are most accurate thanks to additional hardware support for this value. ARM SysTick supports intervals up to 400ms.
Sourcefn reset(&self)
fn reset(&self)
Reset the SchedulerTimer.
This must reset the timer, and can safely disable it and put it in a low
power state. Calling any function other than start()
immediately after
reset()
is invalid.
Implementations should disable the timer and put it in a lower power state. However, not all implementations will be able to guarantee this (for example depending on the underlying hardware or if the timer is implemented on top of a virtualized timer).
Sourcefn arm(&self)
fn arm(&self)
Arm the SchedulerTimer timer and ensure an interrupt will be generated.
The timer must already be started by calling start()
. This function
guarantees that an interrupt will be generated when the already started
timer expires. This interrupt will preempt the running userspace
process.
If the interrupt is already enabled when arm()
is called, this
function should be a no-op implementation.
Sourcefn disarm(&self)
fn disarm(&self)
Disarm the SchedulerTimer timer indicating an interrupt is no longer required.
This does not stop the timer, but indicates to the SchedulerTimer that an interrupt is no longer required (i.e. the process is no longer executing). By not requiring an interrupt this may allow certain implementations to be more efficient by removing the overhead of handling the interrupt.
If the implementation cannot disable the interrupt without stopping the time keeping mechanism, this function should be a no-op implementation.
Sourcefn get_remaining_us(&self) -> Option<NonZeroU32>
fn get_remaining_us(&self) -> Option<NonZeroU32>
Return the number of microseconds remaining in the process’s timeslice if the timeslice is still active.
If the timeslice is still active, this returns Some()
with the number
of microseconds remaining in the timeslice. If the timeslice has
expired, this returns None
.
This function may not be called after it has returned None
(signifying
the timeslice has expired) for a given timeslice until start()
is
called again (to start a new timeslice). If get_remaining_us()
is
called again after returning None
without an intervening call to
start()
, the return value is unspecified and implementations may
return whatever they like.
Implementations on Foreign Types§
Source§impl SchedulerTimer for ()
impl SchedulerTimer for ()
A dummy SchedulerTimer
implementation in which the timer never expires.
Using this implementation is functional, but will mean the scheduler cannot interrupt non-yielding processes.