relm4/channel/component.rs
1// Copyright 2022 System76 <info@system76.com>
2// SPDX-License-Identifier: MIT or Apache-2.0
3
4//! Contains various flavors of channels to send messages between components and workers.
5
6use std::fmt::Debug;
7use std::future::Future;
8use std::sync::Arc;
9
10use crate::component::AsyncComponent;
11use crate::factory::{AsyncFactoryComponent, FactoryComponent};
12use crate::{Component, Sender, ShutdownReceiver};
13
14// Contains senders used by components and factories internally.
15#[derive(Debug)]
16struct ComponentSenderInner<Input, Output, CommandOutput>
17where
18 Input: Debug,
19 CommandOutput: Send + 'static,
20{
21 /// Emits component inputs.
22 input: Sender<Input>,
23 /// Emits component outputs.
24 output: Sender<Output>,
25 /// Emits command outputs.
26 command: Sender<CommandOutput>,
27 shutdown: ShutdownReceiver,
28}
29
30impl<Input, Output, CommandOutput> ComponentSenderInner<Input, Output, CommandOutput>
31where
32 Input: Debug,
33 CommandOutput: Send + 'static,
34{
35 /// Retrieve the sender for input messages.
36 ///
37 /// Useful to forward inputs from another component. If you just need to send input messages,
38 /// [`input()`][Self::input] is more concise.
39 #[must_use]
40 fn input_sender(&self) -> &Sender<Input> {
41 &self.input
42 }
43
44 /// Retrieve the sender for output messages.
45 ///
46 /// Useful to forward outputs from another component. If you just need to send output messages,
47 /// [`output()`][Self::output] is more concise.
48 #[must_use]
49 fn output_sender(&self) -> &Sender<Output> {
50 &self.output
51 }
52
53 /// Retrieve the sender for command output messages.
54 ///
55 /// Useful to forward outputs from another component. If you just need to send output messages,
56 /// [`command()`][Self::command] is more concise.
57 #[must_use]
58 fn command_sender(&self) -> &Sender<CommandOutput> {
59 &self.command
60 }
61
62 /// Emit an input to the component.
63 fn input(&self, message: Input) {
64 // Input messages should always be safe to send
65 // because the runtime keeps the receiver alive.
66 self.input.send(message).expect("The runtime of the component was shutdown. Maybe you accidentally dropped a controller?");
67 }
68
69 /// This is not public because factories can unwrap the result
70 /// because they keep the output receiver alive internally.
71 fn output(&self, message: Output) -> Result<(), Output> {
72 self.output.send(message)
73 }
74
75 /// Spawns an asynchronous command.
76 /// You can bind the the command to the lifetime of the component
77 /// by using a [`ShutdownReceiver`].
78 fn command<Cmd, Fut>(&self, cmd: Cmd)
79 where
80 Cmd: FnOnce(Sender<CommandOutput>, ShutdownReceiver) -> Fut + Send + 'static,
81 Fut: Future<Output = ()> + Send,
82 {
83 let recipient = self.shutdown.clone();
84 let sender = self.command.clone();
85 crate::spawn(async move {
86 cmd(sender, recipient).await;
87 });
88 }
89
90 /// Spawns a synchronous command.
91 ///
92 /// This is particularly useful for CPU-intensive background jobs that
93 /// need to run on a thread-pool in the background.
94 ///
95 /// If you expect the component to be dropped while
96 /// the command is running take care while sending messages!
97 fn spawn_command<Cmd>(&self, cmd: Cmd)
98 where
99 Cmd: FnOnce(Sender<CommandOutput>) + Send + 'static,
100 {
101 let sender = self.command.clone();
102 crate::spawn_blocking(move || cmd(sender));
103 }
104
105 /// Spawns a future that will be dropped as soon as the factory component is shut down.
106 ///
107 /// Essentially, this is a simpler version of [`Self::command()`].
108 fn oneshot_command<Fut>(&self, future: Fut)
109 where
110 Fut: Future<Output = CommandOutput> + Send + 'static,
111 {
112 // Async closures would be awesome here...
113 self.command(move |out, shutdown| {
114 shutdown
115 .register(async move { out.send(future.await) })
116 .drop_on_shutdown()
117 });
118 }
119
120 /// Spawns a synchronous command.
121 ///
122 /// Essentially, this is a simpler version of [`Self::spawn_command()`].
123 fn spawn_oneshot_command<Cmd>(&self, cmd: Cmd)
124 where
125 Cmd: FnOnce() -> CommandOutput + Send + 'static,
126 {
127 let handle = crate::spawn_blocking(cmd);
128 self.oneshot_command(async move { handle.await.unwrap() })
129 }
130}
131
132macro_rules! sender_impl {
133 ($name:ident, $trait:ident) => {
134 /// Contains senders to send and receive messages from a [`Component`].
135 #[derive(Debug)]
136 pub struct $name<C: $trait> {
137 shared: Arc<ComponentSenderInner<C::Input, C::Output, C::CommandOutput>>,
138 }
139
140 impl<C: $trait> $name<C> {
141 pub(crate) fn new(
142 input: Sender<C::Input>,
143 output: Sender<C::Output>,
144 command: Sender<C::CommandOutput>,
145 shutdown: ShutdownReceiver,
146 ) -> Self {
147 Self {
148 shared: Arc::new(ComponentSenderInner {
149 input,
150 output,
151 command,
152 shutdown,
153 }),
154 }
155 }
156
157 /// Retrieve the sender for input messages.
158 ///
159 /// Useful to forward inputs from another component. If you just need to send input messages,
160 /// [`input()`][Self::input] is more concise.
161 #[must_use]
162 pub fn input_sender(&self) -> &Sender<C::Input> {
163 self.shared.input_sender()
164 }
165
166 /// Retrieve the sender for output messages.
167 ///
168 /// Useful to forward outputs from another component. If you just need to send output messages,
169 /// [`output()`][Self::output] is more concise.
170 #[must_use]
171 pub fn output_sender(&self) -> &Sender<C::Output> {
172 self.shared.output_sender()
173 }
174
175 /// Retrieve the sender for command output messages.
176 ///
177 /// Useful to forward outputs from another component. If you just need to send output messages,
178 /// [`command()`][Self::command] is more concise.
179 #[must_use]
180 pub fn command_sender(&self) -> &Sender<C::CommandOutput> {
181 self.shared.command_sender()
182 }
183
184 /// Emit an input to the component.
185 pub fn input(&self, message: C::Input) {
186 self.shared.input(message);
187 }
188
189 /// Emit an output to the component.
190 ///
191 /// Returns [`Err`] if all receivers were dropped,
192 /// for example by [`detach`].
193 ///
194 /// [`detach`]: crate::component::Connector::detach
195 pub fn output(&self, message: C::Output) -> Result<(), C::Output> {
196 self.shared.output(message)
197 }
198
199 /// Spawns an asynchronous command.
200 /// You can bind the the command to the lifetime of the component
201 /// by using a [`ShutdownReceiver`].
202 pub fn command<Cmd, Fut>(&self, cmd: Cmd)
203 where
204 Cmd: FnOnce(Sender<C::CommandOutput>, ShutdownReceiver) -> Fut + Send + 'static,
205 Fut: Future<Output = ()> + Send,
206 {
207 self.shared.command(cmd)
208 }
209
210 /// Spawns a synchronous command.
211 ///
212 /// This is particularly useful for CPU-intensive background jobs that
213 /// need to run on a thread-pool in the background.
214 ///
215 /// If you expect the component to be dropped while
216 /// the command is running take care while sending messages!
217 pub fn spawn_command<Cmd>(&self, cmd: Cmd)
218 where
219 Cmd: FnOnce(Sender<C::CommandOutput>) + Send + 'static,
220 {
221 self.shared.spawn_command(cmd)
222 }
223
224 /// Spawns a future that will be dropped as soon as the factory component is shut down.
225 ///
226 /// Essentially, this is a simpler version of [`Self::command()`].
227 pub fn oneshot_command<Fut>(&self, future: Fut)
228 where
229 Fut: Future<Output = C::CommandOutput> + Send + 'static,
230 {
231 self.shared.oneshot_command(future)
232 }
233
234 /// Spawns a synchronous command that will be dropped as soon as the factory component is shut down.
235 ///
236 /// Essentially, this is a simpler version of [`Self::spawn_command()`].
237 pub fn spawn_oneshot_command<Cmd>(&self, cmd: Cmd)
238 where
239 Cmd: FnOnce() -> C::CommandOutput + Send + 'static,
240 {
241 self.shared.spawn_oneshot_command(cmd)
242 }
243 }
244
245 impl<C: $trait> Clone for $name<C> {
246 fn clone(&self) -> Self {
247 Self {
248 shared: Arc::clone(&self.shared),
249 }
250 }
251 }
252 };
253}
254
255sender_impl!(ComponentSender, Component);
256sender_impl!(AsyncComponentSender, AsyncComponent);
257sender_impl!(FactorySender, FactoryComponent);
258sender_impl!(AsyncFactorySender, AsyncFactoryComponent);