]>
git.proxmox.com Git - rustc.git/blob - vendor/tokio-util/src/time/wheel/level.rs
1 use crate::time
::wheel
::Stack
;
5 /// Wheel for a single level in the timer. This wheel contains 64 slots.
6 pub(crate) struct Level
<T
> {
9 /// Bit field tracking which slots currently contain entries.
11 /// Using a bit field to track slots that contain entries allows avoiding a
12 /// scan to find entries. This field is updated when entries are added or
13 /// removed from a slot.
15 /// The least-significant bit represents slot zero.
19 slot
: [T
; LEVEL_MULT
],
22 /// Indicates when a slot must be processed next.
24 pub(crate) struct Expiration
{
25 /// The level containing the slot.
26 pub(crate) level
: usize,
29 pub(crate) slot
: usize,
31 /// The instant at which the slot needs to be processed.
32 pub(crate) deadline
: u64,
37 /// Being a power of 2 is very important.
38 const LEVEL_MULT
: usize = 64;
40 impl<T
: Stack
> Level
<T
> {
41 pub(crate) fn new(level
: usize) -> Level
<T
> {
42 // Rust's derived implementations for arrays require that the value
43 // contained by the array be `Copy`. So, here we have to manually
44 // initialize every single slot.
55 // It does not look like the necessary traits are
56 // derived for [T; 64].
125 /// Finds the slot that needs to be processed next and returns the slot and
126 /// `Instant` at which this slot must be processed.
127 pub(crate) fn next_expiration(&self, now
: u64) -> Option
<Expiration
> {
128 // Use the `occupied` bit field to get the index of the next slot that
129 // needs to be processed.
130 let slot
= match self.next_occupied_slot(now
) {
135 // From the slot index, calculate the `Instant` at which it needs to be
136 // processed. This value *must* be in the future with respect to `now`.
138 let level_range
= level_range(self.level
);
139 let slot_range
= slot_range(self.level
);
141 // TODO: This can probably be simplified w/ power of 2 math
142 let level_start
= now
- (now
% level_range
);
143 let deadline
= level_start
+ slot
as u64 * slot_range
;
147 "deadline={}; now={}; level={}; slot={}; occupied={:b}",
162 fn next_occupied_slot(&self, now
: u64) -> Option
<usize> {
163 if self.occupied
== 0 {
167 // Get the slot for now using Maths
168 let now_slot
= (now
/ slot_range(self.level
)) as usize;
169 let occupied
= self.occupied
.rotate_right(now_slot
as u32);
170 let zeros
= occupied
.trailing_zeros() as usize;
171 let slot
= (zeros
+ now_slot
) % 64;
176 pub(crate) fn add_entry(&mut self, when
: u64, item
: T
::Owned
, store
: &mut T
::Store
) {
177 let slot
= slot_for(when
, self.level
);
179 self.slot
[slot
].push(item
, store
);
180 self.occupied
|= occupied_bit(slot
);
183 pub(crate) fn remove_entry(&mut self, when
: u64, item
: &T
::Borrowed
, store
: &mut T
::Store
) {
184 let slot
= slot_for(when
, self.level
);
186 self.slot
[slot
].remove(item
, store
);
188 if self.slot
[slot
].is_empty() {
189 // The bit is currently set
190 debug_assert
!(self.occupied
& occupied_bit(slot
) != 0);
193 self.occupied ^
= occupied_bit(slot
);
197 pub(crate) fn pop_entry_slot(&mut self, slot
: usize, store
: &mut T
::Store
) -> Option
<T
::Owned
> {
198 let ret
= self.slot
[slot
].pop(store
);
200 if ret
.is_some() && self.slot
[slot
].is_empty() {
201 // The bit is currently set
202 debug_assert
!(self.occupied
& occupied_bit(slot
) != 0);
204 self.occupied ^
= occupied_bit(slot
);
211 impl<T
> fmt
::Debug
for Level
<T
> {
212 fn fmt(&self, fmt
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
213 fmt
.debug_struct("Level")
214 .field("occupied", &self.occupied
)
219 fn occupied_bit(slot
: usize) -> u64 {
223 fn slot_range(level
: usize) -> u64 {
224 LEVEL_MULT
.pow(level
as u32) as u64
227 fn level_range(level
: usize) -> u64 {
228 LEVEL_MULT
as u64 * slot_range(level
)
231 /// Convert a duration (milliseconds) and a level to a slot position
232 fn slot_for(duration
: u64, level
: usize) -> usize {
233 ((duration
>> (level
* 6)) % LEVEL_MULT
as u64) as usize
236 #[cfg(all(test, not(loom)))]
243 assert_eq
!(pos
as usize, slot_for(pos
, 0));
247 for pos
in level
..64 {
248 let a
= pos
* 64_usize
.pow(level
as u32);
249 assert_eq
!(pos
as usize, slot_for(a
as u64, level
));