# HG changeset patch # User alfadur # Date 1624295482 -10800 # Node ID ed3b510b860c0cfae835d84ce9c0e8a2f42ea856 # Parent c4d931ce2659eba3dfcbd93ac9041a499d237389 add polled timer diff -r c4d931ce2659 -r ed3b510b860c rust/hedgewars-server/src/core.rs --- a/rust/hedgewars-server/src/core.rs Sun Jun 20 16:43:53 2021 +0300 +++ b/rust/hedgewars-server/src/core.rs Mon Jun 21 20:11:22 2021 +0300 @@ -1,5 +1,6 @@ pub mod anteroom; pub mod client; +pub mod events; pub mod indexslab; pub mod room; pub mod server; diff -r c4d931ce2659 -r ed3b510b860c rust/hedgewars-server/src/core/events.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/hedgewars-server/src/core/events.rs Mon Jun 21 20:11:22 2021 +0300 @@ -0,0 +1,110 @@ +use slab::Slab; +use std::{ + convert::TryInto, + iter, + num::NonZeroU32, + time::{Duration, Instant}, +}; + +struct Event { + event_id: u32, + data: Data, +} + +#[derive(Clone)] +pub struct Timeout { + tick_index: u32, + event_index: u32, + event_id: u32, +} + +pub struct TimedEvents { + events: [Slab>; MAX_TIMEOUT], + current_time: Instant, + current_tick_index: u32, + next_event_id: u32, +} + +impl TimedEvents { + pub fn new() -> Self { + Self { + events: iter::repeat_with(|| Slab::new()) + .take(MAX_TIMEOUT) + .collect::>() + .try_into() + .ok() + .unwrap(), + current_time: Instant::now(), + current_tick_index: 0, + next_event_id: 0, + } + } + + pub fn set_timeout(&mut self, seconds_delay: NonZeroU32, data: Data) -> Timeout { + let tick_index = (self.current_tick_index + + std::cmp::min(seconds_delay.get(), MAX_TIMEOUT as u32)) + % MAX_TIMEOUT as u32; + let event_id = self.next_event_id; + self.next_event_id += 1; + let event = Event { event_id, data }; + + let entry = self.events[tick_index as usize].vacant_entry(); + let event_index = entry.key() as u32; + entry.insert(event); + Timeout { + tick_index, + event_index, + event_id, + } + } + + pub fn cancel_timeout(&mut self, timeout: Timeout) -> Option { + let events = &mut self.events[timeout.tick_index as usize]; + if matches!(events.get(timeout.event_index as usize), Some(Event { event_id: id, ..}) if *id == timeout.event_id) + { + Some(events.remove(timeout.event_index as usize).data) + } else { + None + } + } + + pub fn poll(&mut self, time: Instant) -> Vec { + let mut result = vec![]; + let second = Duration::from_secs(1); + while time - self.current_time > second { + self.current_time += second; + self.current_tick_index = (self.current_tick_index + 1) % MAX_TIMEOUT as u32; + result.extend( + self.events[self.current_tick_index as usize] + .drain() + .map(|e| e.data), + ); + } + result + } +} + +mod test { + use super::TimedEvents; + use std::{ + num::NonZeroU32, + time::{Duration, Instant}, + }; + + #[test] + fn events_test() { + let mut events = TimedEvents::::new(); + let now = Instant::now(); + + let timeouts = (1..=3) + .map(|n| events.set_timeout(NonZeroU32::new(n).unwrap(), n)) + .collect::>(); + + let second = Duration::from_secs(1); + assert_eq!(events.cancel_timeout(timeouts[1].clone()), Some(2)); + assert_eq!(events.poll(now + second), vec![1]); + assert!(events.poll(now + second).is_empty()); + assert!(events.poll(now + 2 * second).is_empty()); + assert_eq!(events.poll(now + 3 * second), vec![3]); + } +}