Add simple mailbox
This commit is contained in:
parent
38ec8158dd
commit
a617d21623
114
src/mailbox.rs
Normal file
114
src/mailbox.rs
Normal 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()));
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ use winit::{
|
|||
window::{Window, WindowAttributes},
|
||||
};
|
||||
|
||||
mod mailbox;
|
||||
|
||||
fn main() {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user