add polled timer
authoralfadur
Mon, 21 Jun 2021 20:11:22 +0300
changeset 15799 ed3b510b860c
parent 15798 c4d931ce2659
child 15800 6af892a0a4b8
add polled timer
rust/hedgewars-server/src/core.rs
rust/hedgewars-server/src/core/events.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;
--- /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<Data> {
+    event_id: u32,
+    data: Data,
+}
+
+#[derive(Clone)]
+pub struct Timeout {
+    tick_index: u32,
+    event_index: u32,
+    event_id: u32,
+}
+
+pub struct TimedEvents<Data, const MAX_TIMEOUT: usize> {
+    events: [Slab<Event<Data>>; MAX_TIMEOUT],
+    current_time: Instant,
+    current_tick_index: u32,
+    next_event_id: u32,
+}
+
+impl<Data, const MAX_TIMEOUT: usize> TimedEvents<Data, MAX_TIMEOUT> {
+    pub fn new() -> Self {
+        Self {
+            events: iter::repeat_with(|| Slab::new())
+                .take(MAX_TIMEOUT)
+                .collect::<Vec<_>>()
+                .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<Data> {
+        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<Data> {
+        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::<u32, 30>::new();
+        let now = Instant::now();
+
+        let timeouts = (1..=3)
+            .map(|n| events.set_timeout(NonZeroU32::new(n).unwrap(), n))
+            .collect::<Vec<_>>();
+
+        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]);
+    }
+}