Skip to main content

rustradio/
block.rs

1//! RustRadio Block implementation
2//!
3//! Blocks are the main building blocks of rustradio. They each do one
4//! thing, and you connect them together with streams to process the data.
5use crate::Result;
6use crate::stream::StreamWait;
7
8/// Return type for all blocks.
9///
10/// This will let the scheduler know if more data could come out of this block,
11/// or if it should just never bother calling it again.
12pub enum BlockRet<'a> {
13    /// Everything is fine, but no information about when more data could be
14    /// created.
15    ///
16    /// The graph scheduler should feel free to call the `work` function again
17    /// without waiting or sleeping.
18    ///
19    /// `Again` should not be returned for "polling". In other words, it should
20    /// not be returned repeatedly without data being consumed or produced.
21    ///
22    /// Good examples of returning `Again`:
23    /// * A block finished being in a state (e.g. writing headers), and does not
24    ///   want to deal with restarting `work()` under the new state. Next time
25    ///   `work()` is called, it'll be in a new state, so it's just temporary.
26    ///   Example `AuEncode`.
27    /// * Stream status is checked at the start of `work()`, so instead of
28    ///   re-checking status after a `produce()`/`consume()`, it's easier
29    ///   to just let the graph call `work()` again.
30    ///   Examples: `RtlSdrDecode` and `FirFilter`.
31    ///
32    /// Importantly, in both these examples, a second `work()` call is not
33    /// expected to do nothing, and just return `Again`. It'll either do useful
34    /// work, or it'll properly return a status showing what it's blocked on.
35    ///
36    /// Bad examples of returning `Again`:
37    /// * Can't be bothered identifying the stream we're waiting for.
38    ///
39    /// Returning `Again` indefinitely wastes CPU, and means the graph will
40    /// never finish.
41    Again,
42
43    /// Block didn't produce anything this time, but has a background
44    /// process that may suddenly produce.
45    ///
46    /// The difference between `Again` and `Pending` is that `Pending` implies
47    /// to the graph runner that it's reasonable to sleep a bit before calling
48    /// `work` again. And that activity on any stream won't help either way.
49    ///
50    /// Example: `RtlSdrSource` may not currently have any new data, but we
51    /// can't control when it does.
52    Pending,
53
54    /// Signal that we're waiting for a stream. Either an input or output
55    /// stream.
56    ///
57    /// If a block is waiting for two streams, then pick one for this return. If
58    /// in the next invocation the other stream is the one preventing progress,
59    /// then return that then. Don't worry about not being able to return a
60    /// single status indicating both are being waited for.
61    WaitForStream(&'a dyn StreamWait, usize),
62
63    /// Block indicates that it will never produce more input.
64    ///
65    /// Examples:
66    /// * Reading from file, without repeating, and file reached EOF.
67    /// * Reading from a `VectorSource` that reached its end.
68    /// * Head block reached its max.
69    EOF,
70}
71
72impl std::fmt::Debug for BlockRet<'_> {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
74        write!(
75            f,
76            "{}",
77            match self {
78                BlockRet::Again => "Again".to_string(),
79                BlockRet::Pending => "Pending".to_string(),
80                BlockRet::WaitForStream(_, n) => format!("WaitForStream(_, {n})"),
81                BlockRet::EOF => "EOF".to_string(),
82            }
83        )
84    }
85}
86
87/// Provide name of block.
88///
89/// This has to be a separate trait, because often the `impl` is proc macro
90/// generated, and it's not possible to re-open the same trait `impl` in Rust.
91pub trait BlockName {
92    /// Name of block
93    ///
94    /// Not name of *instance* of block. But it may include the type. E.g.
95    /// `FileSource<Float>`.
96    fn block_name(&self) -> &str;
97}
98
99/// Enable asking if a block is done, and will never return any more data.
100///
101/// This has to be a separate trait, because often the `impl` is proc macro
102/// generated, and it's not possible to re-open the same trait `impl` in Rust.
103pub trait BlockEOF {
104    /// Return EOF status.
105    ///
106    /// Mutable because if eof, the block is also responsible setting EOF on its
107    /// output streams.
108    #[must_use]
109    fn eof(&mut self) -> bool;
110}
111
112/// Block trait. Must be implemented for all blocks.
113///
114/// Simpler blocks can use macros to avoid needing to implement `work()`.
115pub trait Block: BlockName + BlockEOF + Send {
116    /// Block work function
117    ///
118    /// A block implementation keeps track of its own inputs and outputs.
119    ///
120    /// # Errors
121    ///
122    /// If a block work function errors, then it has failed for good. The state
123    /// of the graph as a whole starts becoming meaningless, and the Graph will
124    /// shut down.
125    fn work(&mut self) -> Result<BlockRet<'_>>;
126}
127
128#[cfg(test)]
129#[cfg_attr(coverage_nightly, coverage(off))]
130mod tests {
131    use super::*;
132
133    struct FakeWait {}
134
135    #[async_trait::async_trait]
136    impl StreamWait for FakeWait {
137        fn id(&self) -> usize {
138            123
139        }
140        fn wait(&self, _need: usize) -> bool {
141            true
142        }
143        #[cfg(feature = "async")]
144        async fn wait_async(&self, _need: usize) -> bool {
145            true
146        }
147        fn closed(&self) -> bool {
148            true
149        }
150    }
151
152    #[test]
153    fn blockret_fmt() {
154        assert_eq!(format!("{:?}", BlockRet::Again), "Again");
155        assert_eq!(format!("{:?}", BlockRet::Pending), "Pending");
156        assert_eq!(
157            format!("{:?}", BlockRet::WaitForStream(&FakeWait {}, 1)),
158            "WaitForStream(_, 1)"
159        );
160        assert_eq!(format!("{:?}", BlockRet::EOF), "EOF");
161    }
162}
163/* vim: textwidth=80
164 */