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 */