From a617d2162343ebb64a9271aadaa6f3c57efe6eb1 Mon Sep 17 00:00:00 2001 From: numzero Date: Fri, 10 Jan 2025 23:54:51 +0300 Subject: [PATCH] Add simple mailbox --- src/mailbox.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 + 2 files changed, 116 insertions(+) create mode 100644 src/mailbox.rs diff --git a/src/mailbox.rs b/src/mailbox.rs new file mode 100644 index 0000000..ddb04ef --- /dev/null +++ b/src/mailbox.rs @@ -0,0 +1,114 @@ +use std::{ + future::Future, + sync::Mutex, + task::{Context, Poll, Waker}, +}; + +pub struct Mailbox(Mutex>); + +impl Mailbox { + pub fn new() -> Self { + Mailbox(Mutex::new(MailboxInner { + value: None, + waker: None, + })) + } +} + +struct MailboxInner { + value: Option, + waker: Option, +} + +struct MailboxFuture<'a, T>(&'a Mailbox); +impl<'a, T> Future for MailboxFuture<'a, T> { + type Output = T; + + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut mb = self.0 .0.lock().unwrap(); + if let Some(value) = mb.value.take() { + return Poll::Ready(value); + } + mb.waker = Some(cx.waker().clone()); + Poll::Pending + } +} + +impl Mailbox { + pub fn put(&self, value: T) { + let mut mb = self.0.lock().unwrap(); + mb.value = Some(value); + let Some(waker) = mb.waker.take() else { + return; + }; + drop(mb); + waker.wake(); + } + + pub fn get(&self) -> impl Future + '_ { + MailboxFuture(&self) + } +} + +#[cfg(test)] +mod tests { + use std::{pin::pin, sync::Arc, task::Wake}; + + use super::*; + + struct NoopWaker; + impl Wake for NoopWaker { + fn wake(self: std::sync::Arc) {} + } + + fn ready(f: impl Future) -> bool { + let f = pin!(f); + matches!( + f.poll(&mut Context::from_waker(&Arc::new(NoopWaker).into())), + Poll::Ready(_) + ) + } + + #[test] + fn test_mailbox_empty() { + let mb = Mailbox::::new(); + assert!(!ready(mb.get())); + } + + #[test] + fn test_mailbox_once() { + let mb = Mailbox::::new(); + mb.put(42); + assert_eq!(pollster::block_on(mb.get()), 42); + assert!(!ready(mb.get())); + } + + #[test] + fn test_mailbox_once_oor() { + let mb = Mailbox::::new(); + let f = mb.get(); + mb.put(42); + assert_eq!(pollster::block_on(f), 42); + } + + #[test] + fn test_mailbox_twice_no_wait() { + let mb = Mailbox::::new(); + mb.put(42); + mb.put(13); + assert_eq!(pollster::block_on(mb.get()), 13); + assert!(!ready(mb.get())); + } + + #[test] + fn test_mailbox_twice_in_order() { + let mb = Mailbox::::new(); + let f = mb.get(); + mb.put(42); + assert_eq!(pollster::block_on(f), 42); + let f = mb.get(); + mb.put(13); + assert_eq!(pollster::block_on(f), 13); + assert!(!ready(mb.get())); + } +} diff --git a/src/main.rs b/src/main.rs index 699205b..a70f017 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ use winit::{ window::{Window, WindowAttributes}, }; +mod mailbox; + fn main() { let event_loop = EventLoop::new().unwrap();