use crate::queue::FnOnceQueue;
use crate::time::Instant;
use std::cmp::Ordering;
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::mem;
use std::time::Duration;
#[derive(Copy, Clone, Eq, PartialEq, Default, Debug)]
pub struct FixedTimerKey {
slot: u32,
gen_or_time: u32,
}
#[derive(Copy, Clone, Eq, PartialEq, Default, Debug)]
pub struct MaxTimerKey {
slot: u32, gnn: u32, }
#[derive(Copy, Clone, Eq, PartialEq, Default, Debug)]
pub struct MinTimerKey {
slot: u32, gnn: u32, }
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
struct Time(u64);
impl Time {
pub fn new_ceil(inst: Instant, t0: Instant) -> Self {
let dur = inst.saturating_duration_since(t0);
Self((dur.as_secs() << 16) | u64::from((dur.subsec_nanos() + (1 << 14) - 1) >> 14))
}
pub fn new_floor(inst: Instant, t0: Instant) -> Self {
let dur = inst.saturating_duration_since(t0);
Self((dur.as_secs() << 16) | u64::from(dur.subsec_nanos() >> 14))
}
pub fn add_secs(self, secs: u32) -> Time {
Self(self.0 + (u64::from(secs) << 16))
}
pub fn inc(self) -> Time {
Self(self.0 + 1)
}
pub fn instant(self, t0: Instant) -> Instant {
t0 + Duration::new(self.0 >> 16, ((self.0 & 0xFFFF) as u32) << 14)
}
pub fn wt(self) -> WrapTime {
WrapTime(self.0 as u32)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
struct WrapTime(u32);
impl WrapTime {
fn time(self, base: Time) -> Time {
let val = (base.0 & !0xFFFF_FFFF) | u64::from(self.0);
if val < base.0 {
Time(val + 0x1_0000_0000)
} else {
Time(val)
}
}
}
impl Ord for WrapTime {
fn cmp(&self, other: &Self) -> Ordering {
(self.0.wrapping_sub(other.0) as i32).cmp(&0)
}
}
impl PartialOrd for WrapTime {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
struct TimerKey {
time: WrapTime,
slot: u32,
}
impl TimerKey {
fn new(time: WrapTime, slot: u32) -> Self {
assert_eq!(8, std::mem::size_of::<TimerKey>());
Self { time, slot }
}
}
impl Ord for TimerKey {
fn cmp(&self, other: &Self) -> Ordering {
self.time
.cmp(&other.time)
.then_with(|| self.slot.cmp(&other.slot))
}
}
impl PartialOrd for TimerKey {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
struct VarTimer {
expiry: Time,
curr: Time,
}
enum VarItem {
Max(VarTimer),
Min(VarTimer),
Free(Option<u32>), }
struct VarSlot {
gnn: u32, item: VarItem,
}
pub(crate) type BoxedFnOnce<S> = Box<dyn FnOnce(&mut S) + 'static>;
pub(crate) struct Timers<S> {
t0: Instant,
now: Time,
queue: BTreeMap<TimerKey, BoxedFnOnce<S>>,
var: Vec<VarSlot>,
var_free: Option<u32>,
seq: u32,
}
impl<S: 'static> Timers<S> {
pub(crate) fn new(now: Instant) -> Self {
Self {
t0: now,
now: Time(0),
queue: BTreeMap::new(),
var: Vec::new(),
var_free: None,
seq: 0,
}
}
pub(crate) fn next_expiry(&self) -> Option<Instant> {
self.queue
.iter()
.next()
.map(|(tk, _)| tk.time.time(self.now).instant(self.t0))
}
pub(crate) fn advance(&mut self, now: Instant, queue: &mut FnOnceQueue<S>) {
let target_now = Time::new_floor(now, self.t0);
while self.now < target_now {
let now = self.now.add_secs(0x7FFF).min(target_now);
let key = TimerKey::new(WrapTime(now.wt().0 + 1), 0);
let rest = self.queue.split_off(&key);
let head = mem::replace(&mut self.queue, rest);
self.now = now;
for (key, bfn) in head {
if key.slot >= 0x8000_0000 {
queue.push_box(bfn);
continue;
}
let slot = &mut self.var[key.slot as usize];
match slot.item {
VarItem::Max(ref mut vt) => {
if vt.expiry <= target_now {
queue.push_box(bfn);
self.free_slot(key.slot);
} else {
vt.curr = vt.expiry.min(self.now.add_secs(0x7FFF));
self.queue
.insert(TimerKey::new(vt.curr.wt(), key.slot), bfn);
}
}
VarItem::Min(ref mut vt) => {
if vt.expiry <= target_now {
queue.push_box(bfn);
self.free_slot(key.slot);
} else {
vt.curr =
rounded_75point(self.now, vt.expiry.min(self.now.add_secs(0x7FFF)));
self.queue
.insert(TimerKey::new(vt.curr.wt(), key.slot), bfn);
}
}
VarItem::Free(_) => panic!("TimerKey points to a free slot"),
}
}
}
}
fn alloc_slot(&mut self, item: VarItem) -> (u32, u32) {
if let Some(i) = self.var_free {
let slot = &mut self.var[i as usize];
match slot.item {
VarItem::Free(next) => self.var_free = next,
_ => panic!("Timers: var_free pointed to slot that wasn't free"),
};
slot.item = item;
(i, slot.gnn)
} else {
let i = self.var.len();
if i >= 0x8000_0000 {
panic!("Exceeded 2^31 variable timers at the same time");
}
self.var.push(VarSlot { gnn: 1, item });
(i as u32, 1)
}
}
fn free_slot(&mut self, i: u32) {
let slot = &mut self.var[i as usize];
slot.gnn = slot.gnn.wrapping_add(1).max(1); if let VarItem::Free(_) = slot.item {
panic!("Timers: deleting slot that was already free");
}
slot.item = VarItem::Free(self.var_free);
self.var_free = Some(i);
}
#[cfg(test)]
fn slots_used(&self) -> usize {
let mut rv = self.var.len();
let mut free = self.var_free;
while let Some(curr) = free {
rv -= 1;
match self.var[curr as usize].item {
VarItem::Free(next) => free = next,
_ => panic!("Free slot is not free"),
}
}
rv
}
pub(crate) fn add(&mut self, expiry_time: Instant, bfn: BoxedFnOnce<S>) -> FixedTimerKey {
let expiry = Time::new_ceil(expiry_time, self.t0).max(self.now.inc());
if expiry >= self.now.add_secs(0x7FFF) {
let mk = self.add_max(expiry_time, bfn);
return FixedTimerKey {
slot: mk.slot,
gen_or_time: mk.gnn,
};
}
let mut wt = expiry.wt();
loop {
for _ in 0..0x8000_0000u32 {
self.seq = self.seq.wrapping_add(1);
let slot = self.seq | 0x8000_0000;
if let Entry::Vacant(ent) = self.queue.entry(TimerKey::new(wt, slot)) {
ent.insert(bfn);
return FixedTimerKey {
slot,
gen_or_time: wt.0,
};
}
}
wt.0 = wt.0.wrapping_add(1);
}
}
pub(crate) fn del(&mut self, fk: FixedTimerKey) -> bool {
if fk.slot < 0x8000_0000 {
self.del_max(MaxTimerKey {
slot: fk.slot,
gnn: fk.gen_or_time,
})
} else {
self.queue
.remove(&TimerKey::new(WrapTime(fk.gen_or_time), fk.slot))
.is_some()
}
}
pub(crate) fn add_max(&mut self, expiry_time: Instant, bfn: BoxedFnOnce<S>) -> MaxTimerKey {
let expiry = Time::new_ceil(expiry_time, self.t0);
let curr = expiry.max(self.now.inc()).min(self.now.add_secs(0x7FFF));
let (slot, gnn) = self.alloc_slot(VarItem::Max(VarTimer { expiry, curr }));
self.queue.insert(TimerKey::new(curr.wt(), slot), bfn);
MaxTimerKey { slot, gnn }
}
pub(crate) fn mod_max(&mut self, mk: MaxTimerKey, expiry_time: Instant) -> bool {
if let Some(slot) = self.var.get_mut(mk.slot as usize) {
if slot.gnn == mk.gnn {
if let VarItem::Max(ref mut vt) = slot.item {
let expiry = Time::new_ceil(expiry_time, self.t0);
vt.expiry = vt.expiry.max(expiry);
return true;
}
}
}
false
}
pub(crate) fn del_max(&mut self, mk: MaxTimerKey) -> bool {
if let Some(slot) = self.var.get_mut(mk.slot as usize) {
if slot.gnn == mk.gnn {
if let VarItem::Max(ref mut vt) = slot.item {
self.queue.remove(&TimerKey::new(vt.curr.wt(), mk.slot));
self.free_slot(mk.slot);
return true;
}
}
}
false
}
pub(crate) fn max_is_active(&self, mk: MaxTimerKey) -> bool {
if let Some(slot) = self.var.get(mk.slot as usize) {
slot.gnn == mk.gnn
} else {
false
}
}
pub(crate) fn add_min(&mut self, expiry_time: Instant, bfn: BoxedFnOnce<S>) -> MinTimerKey {
let expiry = Time::new_ceil(expiry_time, self.t0);
let curr = rounded_75point(
self.now.inc(),
expiry.max(self.now.inc()).min(self.now.add_secs(0x7FFF)),
);
let (slot, gnn) = self.alloc_slot(VarItem::Min(VarTimer { expiry, curr }));
self.queue.insert(TimerKey::new(curr.wt(), slot), bfn);
MinTimerKey { slot, gnn }
}
pub(crate) fn mod_min(&mut self, mk: MinTimerKey, expiry_time: Instant) -> bool {
let expiry = Time::new_ceil(expiry_time, self.t0);
if let Some(slot) = self.var.get_mut(mk.slot as usize) {
if slot.gnn == mk.gnn {
if let VarItem::Min(ref mut vt) = slot.item {
if expiry < vt.expiry {
vt.expiry = expiry;
if expiry < vt.curr {
let bfn = self
.queue
.remove(&TimerKey::new(vt.curr.wt(), mk.slot))
.unwrap();
vt.curr = rounded_75point(
self.now.inc(),
expiry.min(self.now.add_secs(0x7FFF)),
);
self.queue.insert(TimerKey::new(vt.curr.wt(), mk.slot), bfn);
}
}
return true;
}
}
}
false
}
pub(crate) fn del_min(&mut self, mk: MinTimerKey) -> bool {
if let Some(slot) = self.var.get_mut(mk.slot as usize) {
if slot.gnn == mk.gnn {
if let VarItem::Min(ref vt) = slot.item {
self.queue.remove(&TimerKey::new(vt.curr.wt(), mk.slot));
self.free_slot(mk.slot);
return true;
}
}
}
false
}
pub(crate) fn min_is_active(&self, mk: MinTimerKey) -> bool {
if let Some(slot) = self.var.get(mk.slot as usize) {
slot.gnn == mk.gnn
} else {
false
}
}
}
fn rounded_75point(t0: Time, t1: Time) -> Time {
let p75 = (t0.0 + 3 * t1.0) >> 2;
let gap = t1.0 - t0.0;
if gap < 0x8000 {
return t1; }
let round = u64::MAX >> gap.leading_zeros() >> 4;
let mut rv = ((p75 - 1) | round) + 1;
if (rv & 0xFFFF) >= 61036 {
rv = (rv & !0xFFFF) + 0x10000;
}
Time(rv)
}
#[cfg(test)]
mod tests;