entry.rs |
Timer state structures.
This module contains the heart of the intrusive timer implementation, and as
such the structures inside are full of tricky concurrency and unsafe code.
# Ground rules
The heart of the timer implementation here is the [`TimerShared`] structure,
shared between the [`TimerEntry`] and the driver. Generally, we permit access
to [`TimerShared`] ONLY via either 1) a mutable reference to [`TimerEntry`] or
2) a held driver lock.
It follows from this that any changes made while holding BOTH 1 and 2 will
be reliably visible, regardless of ordering. This is because of the `acq/rel`
fences on the driver lock ensuring ordering with 2, and rust mutable
reference rules for 1 (a mutable reference to an object can't be passed
between threads without an `acq/rel` barrier, and same-thread we have local
happens-before ordering).
# State field
Each timer has a state field associated with it. This field contains either
the current scheduled time, or a special flag value indicating its state.
This state can either indicate that the timer is on the 'pending' queue (and
thus will be fired with an `Ok(())` result soon) or that it has already been
fired/deregistered.
This single state field allows for code that is firing the timer to
synchronize with any racing `reset` calls reliably.
# Cached vs true timeouts
To allow for the use case of a timeout that is periodically reset before
expiration to be as lightweight as possible, we support optimistically
lock-free timer resets, in the case where a timer is rescheduled to a later
point than it was originally scheduled for.
This is accomplished by lazily rescheduling timers. That is, we update the
state field with the true expiration of the timer from the holder of
the [`TimerEntry`]. When the driver services timers (ie, whenever it's
walking lists of timers), it checks this "true when" value, and reschedules
based on it.
We do, however, also need to track what the expiration time was when we
originally registered the timer; this is used to locate the right linked
list when the timer is being cancelled. This is referred to as the "cached
when" internally.
There is of course a race condition between timer reset and timer
expiration. If the driver fails to observe the updated expiration time, it
could trigger expiration of the timer too early. However, because
[`mark_pending`][mark_pending] performs a compare-and-swap, it will identify this race and
refuse to mark the timer as pending.
[mark_pending]: TimerHandle::mark_pending |
25488 |
handle.rs |
|
1996 |
mod.rs |
Time driver. |
16728 |
source.rs |
|
1194 |
tests |
|
|
wheel |
|
|