Compare commits

..

5 Commits

Author SHA1 Message Date
a1fd74320b Wake up 2025-01-11 00:28:21 +03:00
644b200086 Simplify the mailbox code 2025-01-11 00:26:56 +03:00
8007922721 Use Options instead of a custom enum
It looks bad either way
2025-01-11 00:26:56 +03:00
8ea6684335 A fully async mailbox 2025-01-11 00:26:56 +03:00
a617d21623 Add simple mailbox 2025-01-10 23:54:51 +03:00
2 changed files with 137 additions and 0 deletions

135
src/mailbox.rs Normal file
View File

@ -0,0 +1,135 @@
use std::{
future::Future,
pin::Pin,
sync::{Mutex, MutexGuard},
task::{Context, Poll, Waker},
};
pub struct Mailbox<T>(Mutex<MailboxInner<T>>);
impl<T> Mailbox<T> {
pub fn new() -> Self {
Mailbox(Mutex::new(MailboxInner {
waker: None,
value: None,
}))
}
}
struct MailboxInner<T> {
waker: Option<Waker>,
value: Option<T>,
}
fn wake<T>(mut mb: MutexGuard<MailboxInner<T>>) {
let waker = mb.waker.take();
drop(mb);
if let Some(waker) = waker {
waker.wake();
}
}
struct MailboxPut<'a, T>(&'a Mailbox<T>, Option<T>);
impl<'a, T> Unpin for MailboxPut<'a, T> {}
impl<'a, T> Future for MailboxPut<'a, T> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut mb = self.0 .0.lock().unwrap();
let None = mb.value else {
mb.waker = Some(cx.waker().clone());
return Poll::Pending;
};
mb.value = self.1.take();
wake(mb);
Poll::Ready(())
}
}
struct MailboxGet<'a, T>(&'a Mailbox<T>);
impl<'a, T> Future for MailboxGet<'a, T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut mb = self.0 .0.lock().unwrap();
let Some(value) = mb.value.take() else {
mb.waker = Some(cx.waker().clone());
return Poll::Pending;
};
wake(mb);
Poll::Ready(value)
}
}
impl<T> Mailbox<T> {
pub fn put(&self, value: T) -> impl Future<Output = ()> + '_ {
MailboxPut(&self, Some(value))
}
pub fn get(&self) -> impl Future<Output = T> + '_ {
MailboxGet(&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<Self>) {}
}
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::<i32>::new();
assert!(!ready(mb.get()));
}
#[test]
fn test_mailbox_once() {
let mb = Mailbox::<i32>::new();
pollster::block_on(mb.put(42));
assert_eq!(pollster::block_on(mb.get()), 42);
assert!(!ready(mb.get()));
}
#[test]
fn test_mailbox_once_oor() {
let mb = Mailbox::<i32>::new();
let f = mb.get();
pollster::block_on(mb.put(42));
assert_eq!(pollster::block_on(f), 42);
}
#[test]
fn test_mailbox_twice_no_wait() {
let mb = Mailbox::<i32>::new();
pollster::block_on(mb.put(42));
assert!(!ready(mb.put(13)));
assert_eq!(pollster::block_on(mb.get()), 42);
assert!(!ready(mb.get()));
}
#[test]
fn test_mailbox_twice_in_order() {
let mb = Mailbox::<i32>::new();
let f = mb.get();
pollster::block_on(mb.put(42));
assert_eq!(pollster::block_on(f), 42);
let f = mb.get();
pollster::block_on(mb.put(13));
assert_eq!(pollster::block_on(f), 13);
assert!(!ready(mb.get()));
}
}

View File

@ -6,6 +6,8 @@ use winit::{
window::{Window, WindowAttributes}, window::{Window, WindowAttributes},
}; };
mod mailbox;
fn main() { fn main() {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();