Skip to main content

yash_executor/
forwarder.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2024 WATANABE Yuki
3
4//! Utilities for forwarding the result of a future to another future
5//!
6//! The [`forwarder`] function creates a pair of [`Sender`] and [`Receiver`] that
7//! can be used to forward the result of a future to another future. The sender
8//! half is used to send the result, and the receiver half is used to receive the
9//! result.
10//!
11//! ```
12//! # use yash_executor::forwarder::*;
13//! let (sender, receiver) = forwarder::<u32>();
14//!
15//! // The result is not yet available
16//! assert_eq!(receiver.try_receive(), Err(TryReceiveError::NotSent));
17//!
18//! // Send the result
19//! sender.send(42).unwrap();
20//!
21//! // The result is now available
22//! assert_eq!(receiver.try_receive(), Ok(42));
23//! ```
24//!
25//! If the `Sender` is dropped before sending the result, the `Receiver` will
26//! never receive the result. If the `Receiver` is dropped before receiving the
27//! result, the `Sender` will not be able to send the result, but it does not
28//! otherwise affect the task that produces the result.
29
30use alloc::rc::{Rc, Weak};
31use core::cell::RefCell;
32use core::fmt::Display;
33use core::pin::Pin;
34use core::task::{Context, Poll, Waker};
35
36/// State shared between the sender and receiver
37#[derive(Debug)]
38enum Relay<T> {
39    /// The result has not been computed yet, and the receiver has not been polled.
40    Pending,
41    /// The result has not been computed yet, and the receiver has been polled.
42    Polled(Waker),
43    /// The result has been computed, but the receiver has not received it yet.
44    Computed(T),
45    /// The receiver has received the result.
46    Done,
47}
48
49/// Sender half of the forwarder
50///
51/// See the [module-level documentation](self) for more information.
52#[derive(Debug)]
53pub struct Sender<T> {
54    relay: Weak<RefCell<Relay<T>>>,
55}
56
57/// Receiver half of the forwarder
58///
59/// Call [`try_receive`](Self::try_receive) to examine if the result has been
60/// sent from the sender. `Receiver` also implements the `Future` trait, so you
61/// can use it in an async block or function to receive the result
62/// asynchronously.
63///
64/// See also the [module-level documentation](self) for more information.
65#[derive(Debug)]
66pub struct Receiver<T> {
67    relay: Rc<RefCell<Relay<T>>>,
68}
69
70/// Creates a new forwarder.
71#[must_use]
72pub fn forwarder<T>() -> (Sender<T>, Receiver<T>) {
73    let relay = Rc::new(RefCell::new(Relay::Pending));
74    let sender = Sender {
75        relay: Rc::downgrade(&relay),
76    };
77    let receiver = Receiver { relay };
78    (sender, receiver)
79}
80
81/// Error returned when receiving a value fails
82///
83/// This error may be returned from the [`Receiver::try_receive`] method.
84#[derive(Clone, Copy, Debug, Eq, PartialEq)]
85pub enum TryReceiveError {
86    /// The sender has been dropped, which means the receiver will never receive
87    /// the value.
88    SenderDropped,
89    /// The value has not been sent yet.
90    NotSent,
91    /// The value has already been received.
92    AlreadyReceived,
93}
94
95impl<T> Sender<T> {
96    /// Sends a value to the receiver.
97    ///
98    /// The value is sent to the receiver. If the receiver has been dropped,
99    /// the value is returned back to the caller.
100    ///
101    /// This method consumes the sender, ensuring that the value is sent at most
102    /// once for each sender-receiver pair.
103    pub fn send(self, value: T) -> Result<(), T> {
104        let Some(relay) = self.relay.upgrade() else {
105            return Err(value);
106        };
107
108        let relay = &mut *relay.borrow_mut();
109        match core::mem::replace(relay, Relay::Computed(value)) {
110            Relay::Pending => Ok(()),
111            Relay::Polled(waker) => {
112                waker.wake();
113                Ok(())
114            }
115            // We can send only once, so these cases are impossible
116            Relay::Computed(_) | Relay::Done => unreachable!(),
117        }
118    }
119}
120
121impl<T> Receiver<T> {
122    /// Receives a value from the sender.
123    ///
124    /// This method is similar to [`poll`](Self::poll), but it does not require
125    /// a `Context` argument. If the value has not been sent yet, this method
126    /// returns `Err(TryReceiveError::NotSent)`.
127    pub fn try_receive(&self) -> Result<T, TryReceiveError> {
128        let relay = &mut *self.relay.borrow_mut();
129        match relay {
130            Relay::Pending | Relay::Polled(_) => {
131                if Rc::weak_count(&self.relay) == 0 {
132                    Err(TryReceiveError::SenderDropped)
133                } else {
134                    Err(TryReceiveError::NotSent)
135                }
136            }
137
138            Relay::Computed(_) => {
139                let Relay::Computed(value) = core::mem::replace(relay, Relay::Done) else {
140                    unreachable!()
141                };
142                Ok(value)
143            }
144
145            Relay::Done => Err(TryReceiveError::AlreadyReceived),
146        }
147    }
148}
149
150impl<T> Future for Receiver<T> {
151    type Output = T;
152
153    /// Polls the receiver to receive the value.
154    ///
155    /// This method is similar to [`try_receive`](Self::try_receive), but it
156    /// requires a `Context` argument. If the value has not been sent yet, this
157    /// method returns `Poll::Pending` and stores the `Waker` from the `Context`
158    /// for waking up the current task when the value is sent.
159    ///
160    /// This method should not be called after the value has been received.
161    fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<T> {
162        let relay = &mut *self.relay.borrow_mut();
163        match relay {
164            Relay::Pending | Relay::Polled(_) => {
165                *relay = Relay::Polled(context.waker().clone());
166                Poll::Pending
167            }
168
169            Relay::Computed(_) => {
170                let Relay::Computed(value) = core::mem::replace(relay, Relay::Done) else {
171                    unreachable!()
172                };
173                Poll::Ready(value)
174            }
175
176            Relay::Done => panic!("Receiver polled after receiving the value"),
177        }
178    }
179}
180
181impl Display for TryReceiveError {
182    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
183        match self {
184            TryReceiveError::SenderDropped => "sender already dropped".fmt(f),
185            TryReceiveError::NotSent => "result not sent yet".fmt(f),
186            TryReceiveError::AlreadyReceived => "result already received".fmt(f),
187        }
188    }
189}
190
191// TODO Bump MSRV to 1.81.0 to impl core::error::Error for TryReceiveError