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);