From bb21b6fd7b4b028f38eaa66b6e073137a52e3a30 Mon Sep 17 00:00:00 2001 From: Yaroslav Dynnikov <yaroslav.dynnikov@gmail.com> Date: Fri, 23 Sep 2022 21:04:28 +0300 Subject: [PATCH] doc: mailbox --- src/mailbox.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/mailbox.rs b/src/mailbox.rs index 1648e7fc3e..dadeafaeed 100644 --- a/src/mailbox.rs +++ b/src/mailbox.rs @@ -1,3 +1,10 @@ +//! Another inter-fiber communication primitive. +//! +//! The type [`Mailbox<T>`] is similar to the `tarantool::fiber::Channel<T>`. +//! But unlike channels which are fixed-size, the mailbox holds messages in a +//! growable `Vec<T>`. +//! + use ::tarantool::fiber; use std::cell::RefCell; use std::rc::Rc; @@ -8,6 +15,45 @@ struct Inner<T> { content: RefCell<Vec<T>>, } +/// An inter-fiber communication channel based on `Vec<T>`. +/// +/// One can send messages one by one, transferring the ownership. +/// +/// ``` +/// let mailbox: Mailbox<String> = Mailbox::new(); +/// mailbox.send("Hello".into()); +/// mailbox.send("World".into()); +/// ``` +/// +/// Messages are delivered in the same order as they're sent. +/// +/// Receiving them is possible in two ways: +/// +/// 1. Blocking +/// +/// ``` +/// let mailbox: Mailbox<String> = Mailbox::new(); +/// let timeout = Duration::from_secs(1); +/// +/// mailbox.send("Hello".into()); +/// mailbox.send("World".into()); +/// +/// // Won't yield as mailbox isn't empty. +/// assert_eq!(mailbox.receive_all(timeout), vec!["Hello", "World"]); +/// +/// // Will yield and subsequently timeout. +/// assert_eq!(mailbox.receive_all(timeout), vec![]); +/// ``` +/// +/// 2. Non-blocking +/// +/// ``` +/// let mailbox: Mailbox<()> = Mailbox::new(); +/// +/// // Won't ever yield. +/// assert_eq!(mailbox.try_receive_all(), vec![]); +/// ``` +/// pub struct Mailbox<T>(Rc<Inner<T>>); impl<T> Clone for Mailbox<T> { @@ -24,6 +70,7 @@ impl<T> ::std::fmt::Debug for Mailbox<T> { } impl<T> Mailbox<T> { + /// Creates a new, empty mailbox. pub fn new() -> Self { Self(Rc::new(Inner { cond: Rc::default(), @@ -31,6 +78,8 @@ impl<T> Mailbox<T> { })) } + /// Creates an empty mailbox using the provided `fiber::Cond`. This + /// allows sharing the same `cond` in several places. pub fn with_cond(cond: Rc<fiber::Cond>) -> Self { Self(Rc::new(Inner { cond, @@ -38,15 +87,58 @@ impl<T> Mailbox<T> { })) } + /// Puts `value` into the mailbox and wakes up a pending recipient. + /// + /// The mailbox size is unlimited, so sending a message never blocks + /// the calling fiber. + /// pub fn send(&self, v: T) { self.0.content.borrow_mut().push(v); self.0.cond.signal(); } + /// Checks the mailbox for incoming messages without blocking the + /// caller. + /// + /// Always returns a `Vec<T>`, either empty or not. + /// + /// # Examples + /// ``` + /// let mailbox: Mailbox<i32> = Mailbox::new(); + /// mailbox.send(9); + /// + /// assert_eq!(mailbox.try_receive_all(), vec![9]); // doesn't yield + /// assert_eq!(mailbox.try_receive_all(), vec![]); // doesn't yield either + /// ``` pub fn try_receive_all(&self) -> Vec<T> { self.0.content.take() } + /// Checks the mailbox for incoming messages. If there're none, puts + /// the calling fiber into sleep and yields. The fiber is resumed in + /// any of these events: + /// + /// - Some other fiber sends a message using [`Self::send`]. + /// - Some other fiber signals a `cond` provided in + /// [`Self::with_cond`]. + /// - Upon expiration of the `timeout`. + /// + /// # Yields + /// + /// This function will yield if the mailbox is empty. Even if + /// `timeout` is zero. It won't yield if there're some messages. + /// + /// # Examples + /// + /// ``` + /// let mailbox: Mailbox<i32> = Mailbox::new(); + /// let timeout = Duration::from_secs(1); + /// + /// mailbox.send(7); + /// assert_eq!(mailbox.receive_all(timeout), vec![7]); // doesn't yield + /// assert_eq!(mailbox.receive_all(timeout), vec![]); // yields until timeout expires + /// ``` + /// pub fn receive_all(&self, timeout: Duration) -> Vec<T> { if self.0.content.borrow().is_empty() { self.0.cond.wait_timeout(timeout); -- GitLab