use std::future::Future;
use tokio::sync::{mpsc, oneshot};
use crate::*;
pub struct MessageReceiver<T: Send + 'static> {
receiver: mpsc::Receiver<T>,
}
impl<T: Send + 'static> MessageReceiver<T> {
pub async fn receive(&mut self) -> Option<T> {
self.receiver.recv().await
}
}
pub struct PlainMailboxProcessor<T: Send + 'static> {
sender: mpsc::Sender<T>,
}
impl<T: Send + 'static> Clone for PlainMailboxProcessor<T> {
fn clone(&self) -> Self {
Self {
sender: self.sender.clone(),
}
}
}
impl<T: Send + 'static> PlainMailboxProcessor<T> {
pub fn start<Body, Fut>(body: Body, buffer_size: usize) -> PlainMailboxProcessor<T>
where
Fut: Future<Output = ()> + Send + 'static,
Body: FnOnce(PlainMailboxProcessor<T>, MessageReceiver<T>) -> Fut + Send + 'static,
{
let (tx, rx) = mpsc::channel(buffer_size);
let result = Self { sender: tx };
let mailbox_clone = result.clone();
tokio::spawn(async move {
body(mailbox_clone, MessageReceiver { receiver: rx }).await;
});
result
}
pub fn post_and_forget(&self, msg: T) {
let sender = self.sender.to_owned();
tokio::spawn(async move {
ignore_error(sender.send(msg).await);
});
}
pub async fn post_and_reply<TReply>(
&self,
make_msg: impl FnOnce(ReplyChannel<TReply>) -> T,
) -> Result<TReply> {
let (tx, rx) = oneshot::channel::<TReply>();
let msg = make_msg(ReplyChannel { sender: tx });
self.sender
.send(msg)
.await
.map_err(|_| Error::MailboxStopped)?;
rx.await.map_err(|_| Error::MailboxStopped)
}
}
#[cfg(test)]
mod tests {
use std::{
sync::{Arc, Mutex},
time::Duration,
};
use crate::plain::*;
type MailboxState = Arc<Mutex<i32>>;
enum Message {
Increment(i32, ReplyChannel<()>),
Decrement(i32, ReplyChannel<()>),
Get(ReplyChannel<i32>),
Stop(ReplyChannel<()>),
DelayAndFail,
SendMessageToSelf(i32, ReplyChannel<()>),
ReceiveMessageFromSelf(i32),
}
async fn body(
mb: PlainMailboxProcessor<Message>,
mut recv: MessageReceiver<Message>,
state: MailboxState,
) {
loop {
let msg = recv.receive().await;
match msg {
None => return,
Some(Message::Increment(i, r)) => {
let mut m = state.lock().unwrap();
*m += i;
r.reply(());
}
Some(Message::Decrement(i, r)) => {
let mut m = state.lock().unwrap();
*m -= i;
r.reply(());
}
Some(Message::Get(rep)) => {
let m = state.lock().unwrap();
rep.reply(*m);
}
Some(Message::Stop(r)) => {
r.reply(());
break;
}
Some(Message::DelayAndFail) => {
tokio::time::sleep(Duration::from_secs(10)).await;
panic!("OH MY GOD WE'RE DEAD");
}
Some(Message::SendMessageToSelf(i, r)) => {
mb.post_and_forget(Message::ReceiveMessageFromSelf(i));
r.reply(());
}
Some(Message::ReceiveMessageFromSelf(i)) => {
let mut m = state.lock().unwrap();
*m += i;
}
}
}
}
fn make_mb() -> (PlainMailboxProcessor<Message>, MailboxState) {
let state = Arc::new(Mutex::new(0));
let state_clone = state.clone();
let mb = PlainMailboxProcessor::start(|m, r| body(m, r, state_clone), 100);
(mb, state)
}
#[tokio::test]
async fn messages_arrive_in_mailbox() -> Result<()> {
let (mb, state) = make_mb();
mb.post_and_reply(|r| Message::Increment(1, r)).await?;
assert_eq!(*state.lock().unwrap(), 1);
Ok(())
}
#[tokio::test]
async fn can_get_replies() -> Result<()> {
let (mb, _) = make_mb();
mb.post_and_reply(|r| Message::Increment(5, r)).await?;
let current = mb.post_and_reply(Message::Get).await?;
assert_eq!(current, 5);
Ok(())
}
#[tokio::test]
async fn messages_arrive_in_order() -> Result<()> {
let (mb, _) = make_mb();
mb.post_and_reply(|r| Message::Increment(5, r)).await?;
assert_eq!(mb.post_and_reply(Message::Get).await?, 5);
mb.post_and_reply(|r| Message::Decrement(15, r)).await?;
assert_eq!(mb.post_and_reply(Message::Get).await?, -10);
mb.post_and_reply(|r| Message::Increment(20, r)).await?;
assert_eq!(mb.post_and_reply(Message::Get).await?, 10);
Ok(())
}
#[tokio::test]
async fn post_and_forget_does_not_wait_for_a_result() -> Result<()> {
let (mb, state) = make_mb();
mb.post_and_forget(Message::DelayAndFail);
assert_eq!(*state.lock().unwrap(), 0);
Ok(())
}
#[tokio::test]
async fn can_stop_mailbox_by_returning_from_body_function() -> Result<()> {
let (mb, _) = make_mb();
mb.post_and_reply(|r| Message::Increment(5, r)).await?;
assert_eq!(mb.post_and_reply(Message::Get).await?, 5);
mb.post_and_reply(Message::Stop).await?;
assert_eq!(
mb.post_and_reply(|r| Message::Increment(10, r)).await,
Err(Error::MailboxStopped)
);
Ok(())
}
#[tokio::test]
async fn can_send_message_to_self() -> Result<()> {
let (mb, _) = make_mb();
mb.post_and_reply(|r| Message::SendMessageToSelf(5, r))
.await?;
assert_eq!(mb.post_and_reply(Message::Get).await?, 5);
Ok(())
}
}