Skip to main content

servo_base/generic_channel/
generic_channelset.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4use ipc_channel::ipc::{IpcReceiverSet, IpcSelectionResult};
5use serde::{Deserialize, Serialize};
6use smallvec::SmallVec;
7
8use crate::generic_channel::{GenericReceiver, GenericReceiverVariants, SendError, use_ipc};
9
10/// A GenericReceiverSet. Allows you to wait on multiple GenericReceivers.
11/// Automatically selects either Ipc or crossbeam depending on multiprocess mode.
12/// # Examples
13/// ```ignore
14/// let mut rx_set = GenericReceiverSet::new();
15/// let private_channel = generic_channel::channel().unwrap();
16/// let public_channel = generic_channel::channel().unwrap();
17/// let reporter_channel = generic_channel::channel().unwrap();
18/// let private_id = rx_set.add(private_receiver);
19/// let public_id = rx_set.add(public_receiver);
20/// let reporter_id = rx_set.add(memory_reporter);
21/// for received in rx_set.select().into_iter() {
22///     match received {
23///         GenericSelectionResult::ChannelClosed(_) => continue,
24///         GenericSelectionResult::Error => println!("Found selection error"),
25///         GenericSelectionResult::MessageReceived(id, msg) => { /*...*/ }
26///     }
27/// }
28/// ```
29pub struct GenericReceiverSet<T: Serialize + for<'de> Deserialize<'de>>(
30    GenericReceiverSetVariants<T>,
31);
32
33impl<T: Serialize + for<'de> Deserialize<'de>> Default for GenericReceiverSet<T> {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38enum GenericReceiverSetVariants<T: for<'de> Deserialize<'de>> {
39    Ipc(ipc_channel::ipc::IpcReceiverSet),
40    Crossbeam(Vec<crossbeam_channel::Receiver<Result<T, SendError>>>),
41}
42
43#[cfg(test)]
44pub fn create_ipc_receiver_set<T: Serialize + for<'de> serde::Deserialize<'de>>()
45-> GenericReceiverSet<T> {
46    GenericReceiverSet(GenericReceiverSetVariants::Ipc(
47        IpcReceiverSet::new().expect("Could not create ipc receiver"),
48    ))
49}
50
51#[cfg(test)]
52pub fn create_crossbeam_receiver_set<T: Serialize + for<'de> serde::Deserialize<'de>>()
53-> GenericReceiverSet<T> {
54    GenericReceiverSet(GenericReceiverSetVariants::Crossbeam(vec![]))
55}
56
57/// Result for readable events returned from [GenericReceiverSet::select].
58#[derive(Debug, PartialEq)]
59pub enum GenericSelectionResult<T> {
60    /// A message received from the [`GenericReceiver`],
61    /// identified by the `u64` value and Deserialized into a 'T'.
62    MessageReceived(u64, T),
63    /// The channel has been closed for the [GenericReceiver] identified by the `u64` value.
64    ChannelClosed(u64),
65    /// An error occurred decoding the message.
66    Error(String),
67}
68
69impl<T: Serialize + for<'de> serde::Deserialize<'de>> From<IpcSelectionResult>
70    for GenericSelectionResult<T>
71{
72    fn from(value: IpcSelectionResult) -> Self {
73        match value {
74            IpcSelectionResult::MessageReceived(channel_id, ipc_message) => {
75                match ipc_message.to() {
76                    Ok(value) => GenericSelectionResult::MessageReceived(channel_id, value),
77                    Err(error) => GenericSelectionResult::Error(error.to_string()),
78                }
79            },
80            IpcSelectionResult::ChannelClosed(channel_id) => {
81                GenericSelectionResult::ChannelClosed(channel_id)
82            },
83        }
84    }
85}
86
87impl<T: Serialize + for<'de> Deserialize<'de>> GenericReceiverSet<T> {
88    /// Create a new ReceiverSet.
89    pub fn new() -> GenericReceiverSet<T> {
90        if use_ipc() {
91            GenericReceiverSet(GenericReceiverSetVariants::Ipc(
92                IpcReceiverSet::new().expect("Could not create ipc receiver"),
93            ))
94        } else {
95            GenericReceiverSet(GenericReceiverSetVariants::Crossbeam(vec![]))
96        }
97    }
98
99    /// Add a receiver to the set.
100    pub fn add(&mut self, receiver: GenericReceiver<T>) -> u64 {
101        match (&mut self.0, receiver.0) {
102            (
103                GenericReceiverSetVariants::Ipc(ipc_receiver_set),
104                GenericReceiverVariants::Ipc(ipc_receiver),
105            ) => ipc_receiver_set
106                .add(ipc_receiver)
107                .expect("Could not add channel"),
108            (GenericReceiverSetVariants::Ipc(_), GenericReceiverVariants::Crossbeam(_)) => {
109                unreachable!()
110            },
111            (GenericReceiverSetVariants::Crossbeam(_), GenericReceiverVariants::Ipc(_)) => {
112                unreachable!()
113            },
114            (
115                GenericReceiverSetVariants::Crossbeam(receivers),
116                GenericReceiverVariants::Crossbeam(receiver),
117            ) => {
118                let index = receivers.len() as u64;
119                receivers.push(receiver);
120                index
121            },
122        }
123    }
124
125    /// Create a [`Selector`] that owns the underlying select state.
126    ///
127    /// # Examples
128    ///
129    /// ```no_run
130    ///  use servo_base::generic_channel::{self, GenericReceiverSet};
131    ///
132    ///  let (_, receiver_one) = generic_channel::channel::<()>().unwrap();
133    ///  let (_, receiver_two) = generic_channel::channel::<()>().unwrap();
134    ///  let mut rx_set = GenericReceiverSet::<()>::new();
135    ///  let _select_idx_1 = rx_set.add(receiver_one);
136    ///  let _select_idx_2 = rx_set.add(receiver_two);
137    ///  // Build the Selector once, before the loop if all receivers are known in advance.
138    ///  let mut selector = rx_set.selector();
139    ///  loop {
140    ///    for received in selector.select().into_iter() {
141    ///      // do something
142    ///    }
143    ///  }
144    /// ```
145    pub fn selector(&mut self) -> Selector<'_, T> {
146        let inner = match &mut self.0 {
147            GenericReceiverSetVariants::Ipc(set) => SelectorInner::Ipc(set),
148            GenericReceiverSetVariants::Crossbeam(receivers) => {
149                let mut sel = crossbeam_channel::Select::new();
150                for receiver in receivers.iter() {
151                    sel.recv(receiver);
152                }
153                SelectorInner::Crossbeam {
154                    receivers: receivers.as_slice(),
155                    sel,
156                }
157            },
158        };
159        Selector { inner }
160    }
161
162    /// One-shot select. Builds a [`Selector`], runs select once and drops it.
163    ///
164    /// For usage in loops consider using [`GenericReceiverSet::selector()`] to build the selector
165    /// once upfront.
166    pub fn select(&mut self) -> SmallVec<[GenericSelectionResult<T>; 2]> {
167        self.selector().select()
168    }
169}
170
171/// Borrows of a [`GenericReceiverSet`] used to drive repeated `select` calls
172/// without rebuilding the underlying `crossbeam_channel::Select` each time.
173/// See [`GenericReceiverSet::selector`].
174pub struct Selector<'a, T: Serialize + for<'de> Deserialize<'de>> {
175    inner: SelectorInner<'a, T>,
176}
177
178enum SelectorInner<'a, T: for<'de> Deserialize<'de>> {
179    Ipc(&'a mut IpcReceiverSet),
180    Crossbeam {
181        receivers: &'a [crossbeam_channel::Receiver<Result<T, SendError>>],
182        sel: crossbeam_channel::Select<'a>,
183    },
184}
185
186impl<'a, T: Serialize + for<'de> Deserialize<'de>> Selector<'a, T> {
187    /// Block until at least one of the Receivers receives a message.
188    ///
189    /// Note: The IPC variant can return multiple results in one call.
190    /// The crossbeam variant always returns exactly one.
191    pub fn select(&mut self) -> SmallVec<[GenericSelectionResult<T>; 2]> {
192        match &mut self.inner {
193            SelectorInner::Ipc(ipc_receiver_set) => ipc_receiver_set
194                .select()
195                .map(|result_value| {
196                    result_value
197                        .into_iter()
198                        .map(|selection_result| selection_result.into())
199                        .collect()
200                })
201                .unwrap_or_else(|e| {
202                    smallvec::smallvec![GenericSelectionResult::Error(e.to_string())]
203                }),
204            SelectorInner::Crossbeam { receivers, sel } => {
205                let selected = sel.select();
206                let index = selected.index();
207                let selection_result = match receivers.get(index) {
208                    None => GenericSelectionResult::ChannelClosed(index as u64),
209                    Some(receiver) => match selected.recv(receiver) {
210                        Ok(Ok(value)) => {
211                            GenericSelectionResult::MessageReceived(index as u64, value)
212                        },
213                        Ok(Err(error)) => GenericSelectionResult::Error(error.to_string()),
214                        Err(_) => GenericSelectionResult::ChannelClosed(index as u64),
215                    },
216                };
217                smallvec::smallvec![selection_result]
218            },
219        }
220    }
221}