Add simple mailbox

This commit is contained in:
numzero 2025-01-10 23:54:51 +03:00
parent 38ec8158dd
commit a617d21623
2 changed files with 116 additions and 0 deletions

114
src/mailbox.rs Normal file
View File

@ -0,0 +1,114 @@
use std::{
future::Future,
sync::Mutex,
task::{Context, Poll, Waker},
};
pub struct Mailbox<T>(Mutex<MailboxInner<T>>);
impl<T> Mailbox<T> {
pub fn new() -> Self {
Mailbox(Mutex::new(MailboxInner {
value: None,
waker: None,
}))
}
}
struct MailboxInner<T> {
value: Option<T>,
waker: Option<Waker>,
}
struct MailboxFuture<'a, T>(&'a Mailbox<T>);
impl<'a, T> Future for MailboxFuture<'a, T> {
type Output = T;
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
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<T> Mailbox<T> {
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<Output = T> + '_ {
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<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();
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();
mb.put(42);
assert_eq!(pollster::block_on(f), 42);
}
#[test]
fn test_mailbox_twice_no_wait() {
let mb = Mailbox::<i32>::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::<i32>::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()));
}
}

View File

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