Skip to main content

servo_base/generic_channel/
mod.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/. */
4
5//! Enum wrappers to be able to select different channel implementations at runtime.
6
7use std::fmt::Display;
8use std::marker::PhantomData;
9use std::panic::Location;
10#[cfg(feature = "multiprocess")]
11use std::sync::OnceLock;
12use std::time::Duration;
13use std::{fmt, io};
14
15use crossbeam_channel::RecvTimeoutError;
16use ipc_channel::router::ROUTER;
17use ipc_channel::{IpcError, SerDeError};
18use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
19use malloc_size_of_derive::MallocSizeOf;
20use serde::de::VariantAccess;
21use serde::{Deserialize, Deserializer, Serialize, Serializer};
22
23mod callback;
24pub use callback::GenericCallback;
25mod lazy_callback;
26pub use lazy_callback::{CallbackSetter, LazyCallback, lazy_callback};
27mod oneshot;
28mod shared_memory;
29pub use oneshot::{GenericOneshotReceiver, GenericOneshotSender, oneshot};
30pub use shared_memory::GenericSharedMemory;
31mod generic_channelset;
32pub use generic_channelset::{GenericReceiverSet, GenericSelectionResult};
33mod buffered;
34pub use buffered::GenericBufferedSender;
35
36/// Cache for being in Ipc Mode
37#[cfg(feature = "multiprocess")]
38static USE_IPC: OnceLock<bool> = OnceLock::new();
39
40/// Return if we should be in IPC Mode
41#[cfg(feature = "multiprocess")]
42fn use_ipc() -> bool {
43    *USE_IPC.get_or_init(|| {
44        servo_config::opts::get().multiprocess || servo_config::opts::get().force_ipc
45    })
46}
47
48#[cfg(not(feature = "multiprocess"))]
49fn use_ipc() -> bool {
50    false
51}
52
53/// Abstraction of the ability to send a particular type of message cross-process.
54/// This can be used to ease the use of GenericSender sub-fields.
55pub trait GenericSend<T>
56where
57    T: serde::Serialize + for<'de> serde::Deserialize<'de>,
58{
59    /// send message T
60    fn send(&self, _: T) -> SendResult;
61
62    /// Send a message T and log any error (instead of returning it).
63    ///
64    /// In cases where channel closure is possible (because the receiver does not exist anymore),
65    /// this convenience method can be used to ignore the result and log the error as a warning.
66    #[track_caller]
67    fn send_or_warn(&self, message: T) {
68        if let Err(error) = self.send(message) {
69            let location = Location::caller();
70            log::warn!("Failed to send msg due to `{error}` at {location:?}");
71        }
72    }
73
74    /// Send a message T and ignore the result
75    ///
76    /// In cases where channel closure is expected to happen intermittently, and the sender
77    /// doesn't care about the result, this is a short form for `let _ = GenericSend::send();`,
78    /// which makes the intent clearer.
79    fn send_or_ignore(&self, message: T) {
80        let _ = self.send(message);
81    }
82
83    /// get underlying sender
84    fn sender(&self) -> GenericSender<T>;
85}
86
87/// A GenericSender that sends messages to a [GenericReceiver].
88///
89/// The sender supports sending messages cross-process, if servo is run in multiprocess mode.
90pub struct GenericSender<T: Serialize>(GenericSenderVariants<T>);
91
92/// The actual GenericSender variant.
93///
94/// This enum is private, so that outside code can't construct a GenericSender itself.
95/// This ensures that users can't construct a crossbeam variant in multiprocess mode.
96enum GenericSenderVariants<T: Serialize> {
97    Ipc(ipc_channel::ipc::IpcSender<T>),
98    /// A crossbeam-channel. To keep the API in sync with the Ipc variant when using a Router,
99    /// which propagates the IPC error, the inner type is a Result.
100    /// In the IPC case, the Router deserializes the message, which can fail, and sends
101    /// the result to a crossbeam receiver.
102    /// The crossbeam channel does not involve serializing, so we can't have this error,
103    /// but replicating the API allows us to have one channel type as the receiver
104    /// after routing the receiver .
105    Crossbeam(crossbeam_channel::Sender<Result<T, SendError>>),
106}
107
108fn serialize_generic_sender_variants<T: Serialize, S: Serializer>(
109    value: &GenericSenderVariants<T>,
110    s: S,
111) -> Result<S::Ok, S::Error> {
112    match value {
113        GenericSenderVariants::Ipc(sender) => {
114            s.serialize_newtype_variant("GenericSender", 0, "Ipc", sender)
115        },
116        // All GenericSenders will be IPC channels in multi-process mode, so sending a
117        // GenericChannel over existing IPC channels is no problem and won't fail.
118        // In single-process mode, we can also send GenericSenders over other GenericSenders
119        // just fine, since no serialization is required.
120        // The only reason we need / want serialization is to support sending GenericSenders
121        // over existing IPC channels **in single process mode**. This allows us to
122        // incrementally port channels to the GenericChannel, without needing to follow a
123        // top-to-bottom approach.
124        // Long-term we can remove this branch in the code again and replace it with
125        // unreachable, since likely all IPC channels would be GenericChannels.
126        GenericSenderVariants::Crossbeam(sender) => {
127            if use_ipc() {
128                return Err(serde::ser::Error::custom(
129                    "Crossbeam channel found in multiprocess mode!",
130                ));
131            } // We know everything is in one address-space, so we can "serialize" the sender by
132            // sending a leaked Box pointer.
133            let sender_clone_addr = Box::into_raw(Box::new(sender.clone())) as *mut _ as usize;
134            s.serialize_newtype_variant("GenericSender", 1, "Crossbeam", &sender_clone_addr)
135        },
136    }
137}
138
139impl<T: Serialize> Serialize for GenericSender<T> {
140    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
141        serialize_generic_sender_variants(&self.0, s)
142    }
143}
144
145struct GenericSenderVisitor<T> {
146    marker: PhantomData<T>,
147}
148
149impl<'de, T: Serialize + Deserialize<'de>> serde::de::Visitor<'de> for GenericSenderVisitor<T> {
150    type Value = GenericSenderVariants<T>;
151
152    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
153        formatter.write_str("a GenericSender variant")
154    }
155
156    fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
157    where
158        A: serde::de::EnumAccess<'de>,
159    {
160        #[derive(Deserialize)]
161        enum GenericSenderVariantNames {
162            Ipc,
163            Crossbeam,
164        }
165
166        let (variant_name, variant_data): (GenericSenderVariantNames, _) = data.variant()?;
167
168        match variant_name {
169            GenericSenderVariantNames::Ipc => variant_data
170                .newtype_variant::<ipc_channel::ipc::IpcSender<T>>()
171                .map(|sender| GenericSenderVariants::Ipc(sender)),
172            GenericSenderVariantNames::Crossbeam => {
173                if use_ipc() {
174                    return Err(serde::de::Error::custom(
175                        "Crossbeam channel found in multiprocess mode!",
176                    ));
177                }
178                let addr = variant_data.newtype_variant::<usize>()?;
179                let ptr = addr as *mut crossbeam_channel::Sender<Result<T, SendError>>;
180                // SAFETY: We know we are in the same address space as the sender, so we can safely
181                // reconstruct the Box.
182                #[expect(unsafe_code)]
183                let sender = unsafe { Box::from_raw(ptr) };
184                Ok(GenericSenderVariants::Crossbeam(*sender))
185            },
186        }
187    }
188}
189
190impl<'a, T: Serialize + Deserialize<'a>> Deserialize<'a> for GenericSender<T> {
191    fn deserialize<D>(d: D) -> Result<GenericSender<T>, D::Error>
192    where
193        D: Deserializer<'a>,
194    {
195        d.deserialize_enum(
196            "GenericSender",
197            &["Ipc", "Crossbeam"],
198            GenericSenderVisitor {
199                marker: PhantomData,
200            },
201        )
202        .map(|variant| GenericSender(variant))
203    }
204}
205
206impl<T> Clone for GenericSender<T>
207where
208    T: Serialize,
209{
210    fn clone(&self) -> Self {
211        match &self.0 {
212            GenericSenderVariants::Ipc(chan) => {
213                GenericSender(GenericSenderVariants::Ipc(chan.clone()))
214            },
215            GenericSenderVariants::Crossbeam(chan) => {
216                GenericSender(GenericSenderVariants::Crossbeam(chan.clone()))
217            },
218        }
219    }
220}
221
222impl<T: Serialize> fmt::Debug for GenericSender<T> {
223    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224        write!(f, "Sender(..)")
225    }
226}
227
228impl<T: Serialize> GenericSender<T> {
229    #[inline]
230    pub fn send(&self, msg: T) -> SendResult {
231        match &self.0 {
232            GenericSenderVariants::Ipc(sender) => sender
233                .send(msg)
234                .map_err(|e| SendError::SerializationError(e.to_string())),
235            GenericSenderVariants::Crossbeam(sender) => {
236                sender.send(Ok(msg)).map_err(|_| SendError::Disconnected)
237            },
238        }
239    }
240
241    /// Send a message T and log any error (instead of returning it).
242    ///
243    /// In cases where channel closure is possible (because the receiver does not exist anymore),
244    /// this convenience method can be used to ignore the result and log the error as a warning.
245    #[inline]
246    pub fn send_or_warn(&self, msg: T) {
247        if let Err(error) = self.send(msg) {
248            let location = Location::caller();
249            log::warn!("Failed to send msg due to `{error}` at {location:?}");
250        }
251    }
252
253    /// Send a message T and ignore the result
254    ///
255    /// In cases where channel closure is expected to happen intermittently, and the sender
256    /// doesn't care about the result, this is a short form for `let _ = GenericSender::send();`,
257    /// which makes the intent clearer.
258    #[inline]
259    pub fn send_or_ignore(&self, msg: T) {
260        let _ = self.send(msg);
261    }
262}
263
264impl<T: Serialize> MallocSizeOf for GenericSender<T> {
265    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
266        match &self.0 {
267            GenericSenderVariants::Ipc(ipc_sender) => ipc_sender.size_of(ops),
268            GenericSenderVariants::Crossbeam(sender) => sender.size_of(ops),
269        }
270    }
271}
272
273#[derive(Debug)]
274pub enum SendError {
275    Disconnected,
276    SerializationError(String),
277}
278
279impl From<IpcError> for SendError {
280    fn from(value: IpcError) -> Self {
281        match value {
282            IpcError::SerializationError(ser_de_error) => {
283                SendError::SerializationError(ser_de_error.to_string())
284            },
285            IpcError::Io(error) => {
286                log::error!("IO Error in ipc {:?}", error);
287                SendError::Disconnected
288            },
289            IpcError::Disconnected => SendError::Disconnected,
290        }
291    }
292}
293
294impl From<SerDeError> for SendError {
295    fn from(value: SerDeError) -> Self {
296        SendError::SerializationError(value.to_string())
297    }
298}
299
300impl From<io::Error> for SendError {
301    fn from(value: io::Error) -> Self {
302        log::error!("IO Error in IPC {:?}", value);
303        SendError::Disconnected
304    }
305}
306
307impl Display for SendError {
308    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
309        write!(f, "{self:?}")
310    }
311}
312
313pub type SendResult = Result<(), SendError>;
314
315#[derive(Debug)]
316pub enum ReceiveError {
317    DeserializationFailed(String),
318    /// Io Error. May occur when using IPC.
319    Io(std::io::Error),
320    /// The channel was closed.
321    Disconnected,
322}
323
324impl From<IpcError> for ReceiveError {
325    fn from(e: IpcError) -> Self {
326        match e {
327            IpcError::Disconnected => ReceiveError::Disconnected,
328            IpcError::Io(reason) => ReceiveError::Io(reason),
329            IpcError::SerializationError(ser_de_error) => {
330                ReceiveError::DeserializationFailed(ser_de_error.to_string())
331            },
332        }
333    }
334}
335
336impl From<crossbeam_channel::RecvError> for ReceiveError {
337    fn from(_: crossbeam_channel::RecvError) -> Self {
338        ReceiveError::Disconnected
339    }
340}
341
342impl fmt::Display for ReceiveError {
343    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
344        match *self {
345            ReceiveError::DeserializationFailed(ref error) => {
346                write!(fmt, "deserialization error: {error}")
347            },
348            ReceiveError::Io(ref error) => write!(fmt, "io error: {error}"),
349            ReceiveError::Disconnected => write!(fmt, "disconnected"),
350        }
351    }
352}
353impl From<std::io::Error> for ReceiveError {
354    fn from(value: std::io::Error) -> Self {
355        ReceiveError::Io(value)
356    }
357}
358
359pub enum TryReceiveError {
360    Empty,
361    ReceiveError(ReceiveError),
362}
363
364impl From<crossbeam_channel::RecvTimeoutError> for TryReceiveError {
365    fn from(value: crossbeam_channel::RecvTimeoutError) -> Self {
366        match value {
367            RecvTimeoutError::Timeout => TryReceiveError::Empty,
368            RecvTimeoutError::Disconnected => {
369                TryReceiveError::ReceiveError(ReceiveError::Disconnected)
370            },
371        }
372    }
373}
374
375impl From<ipc_channel::TryRecvError> for TryReceiveError {
376    fn from(e: ipc_channel::TryRecvError) -> Self {
377        match e {
378            ipc_channel::TryRecvError::Empty => TryReceiveError::Empty,
379            ipc_channel::TryRecvError::IpcError(inner) => {
380                TryReceiveError::ReceiveError(inner.into())
381            },
382        }
383    }
384}
385
386impl From<crossbeam_channel::TryRecvError> for TryReceiveError {
387    fn from(e: crossbeam_channel::TryRecvError) -> Self {
388        match e {
389            crossbeam_channel::TryRecvError::Empty => TryReceiveError::Empty,
390            crossbeam_channel::TryRecvError::Disconnected => {
391                TryReceiveError::ReceiveError(ReceiveError::Disconnected)
392            },
393        }
394    }
395}
396
397pub type RoutedReceiver<T> = crossbeam_channel::Receiver<Result<T, SendError>>;
398pub type ReceiveResult<T> = Result<T, ReceiveError>;
399pub type TryReceiveResult<T> = Result<T, TryReceiveError>;
400pub type RoutedReceiverReceiveResult<T> =
401    Result<Result<T, SendError>, crossbeam_channel::RecvError>;
402
403pub fn to_receive_result<T>(receive_result: RoutedReceiverReceiveResult<T>) -> ReceiveResult<T> {
404    match receive_result {
405        Ok(Ok(msg)) => Ok(msg),
406        Err(_crossbeam_recv_err) => Err(ReceiveError::Disconnected),
407        Ok(Err(ipc_err)) => Err(ReceiveError::DeserializationFailed(ipc_err.to_string())),
408    }
409}
410
411#[derive(MallocSizeOf)]
412pub struct GenericReceiver<T>(GenericReceiverVariants<T>)
413where
414    T: for<'de> Deserialize<'de> + Serialize;
415
416impl<T> std::fmt::Debug for GenericReceiver<T>
417where
418    T: for<'de> Deserialize<'de> + Serialize,
419{
420    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421        f.debug_tuple("GenericReceiver").finish()
422    }
423}
424
425#[derive(MallocSizeOf)]
426enum GenericReceiverVariants<T>
427where
428    T: for<'de> Deserialize<'de> + Serialize,
429{
430    Ipc(ipc_channel::ipc::IpcReceiver<T>),
431    Crossbeam(RoutedReceiver<T>),
432}
433
434impl<T> GenericReceiver<T>
435where
436    T: for<'de> Deserialize<'de> + Serialize,
437{
438    #[inline]
439    pub fn recv(&self) -> ReceiveResult<T> {
440        match &self.0 {
441            GenericReceiverVariants::Ipc(receiver) => Ok(receiver.recv()?),
442            GenericReceiverVariants::Crossbeam(receiver) => {
443                // `recv()` returns an error if the channel is disconnected
444                let msg = receiver.recv()?;
445                // `msg` must be `ok` because the corresponding [`GenericSender::Crossbeam`] will
446                // unconditionally send an `Ok(T)`
447                Ok(msg.expect("Infallible"))
448            },
449        }
450    }
451
452    #[inline]
453    pub fn try_recv(&self) -> TryReceiveResult<T> {
454        match &self.0 {
455            GenericReceiverVariants::Ipc(receiver) => Ok(receiver.try_recv()?),
456            GenericReceiverVariants::Crossbeam(receiver) => {
457                let msg = receiver.try_recv()?;
458                Ok(msg.expect("Infallible"))
459            },
460        }
461    }
462
463    /// Blocks up to the specific duration attempting to receive a message.
464    #[inline]
465    pub fn try_recv_timeout(&self, timeout: Duration) -> Result<T, TryReceiveError> {
466        match &self.0 {
467            GenericReceiverVariants::Ipc(ipc_receiver) => {
468                ipc_receiver.try_recv_timeout(timeout).map_err(|e| e.into())
469            },
470            GenericReceiverVariants::Crossbeam(receiver) => match receiver.recv_timeout(timeout) {
471                Ok(Ok(value)) => Ok(value),
472                Ok(Err(_)) => unreachable!("Infallable"),
473                Err(RecvTimeoutError::Disconnected) => {
474                    Err(TryReceiveError::ReceiveError(ReceiveError::Disconnected))
475                },
476                Err(RecvTimeoutError::Timeout) => Err(TryReceiveError::Empty),
477            },
478        }
479    }
480
481    /// Route to a crossbeam receiver, preserving any errors.
482    ///
483    /// For `Crossbeam` receivers this is a no-op, while for `Ipc` receivers
484    /// this creates a route.
485    #[inline]
486    pub fn route_preserving_errors(self) -> RoutedReceiver<T>
487    where
488        T: Send + 'static,
489    {
490        match self.0 {
491            GenericReceiverVariants::Ipc(ipc_receiver) => {
492                let (crossbeam_sender, crossbeam_receiver) = crossbeam_channel::unbounded();
493                let crossbeam_sender_clone = crossbeam_sender;
494                ROUTER.add_typed_route(
495                    ipc_receiver,
496                    Box::new(move |message| {
497                        let _ = crossbeam_sender_clone.send(message.map_err(|e| e.into()));
498                    }),
499                );
500                crossbeam_receiver
501            },
502            GenericReceiverVariants::Crossbeam(receiver) => receiver,
503        }
504    }
505}
506
507impl<T> Serialize for GenericReceiver<T>
508where
509    T: for<'de> Deserialize<'de> + Serialize,
510{
511    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
512        match &self.0 {
513            GenericReceiverVariants::Ipc(receiver) => {
514                s.serialize_newtype_variant("GenericReceiver", 0, "Ipc", receiver)
515            },
516            GenericReceiverVariants::Crossbeam(receiver) => {
517                if use_ipc() {
518                    return Err(serde::ser::Error::custom(
519                        "Crossbeam channel found in multiprocess mode!",
520                    ));
521                } // We know everything is in one address-space, so we can "serialize" the receiver by
522                // sending a leaked Box pointer.
523                let receiver_clone_addr =
524                    Box::into_raw(Box::new(receiver.clone())) as *mut _ as usize;
525                s.serialize_newtype_variant("GenericReceiver", 1, "Crossbeam", &receiver_clone_addr)
526            },
527        }
528    }
529}
530
531struct GenericReceiverVisitor<T> {
532    marker: PhantomData<T>,
533}
534impl<'de, T> serde::de::Visitor<'de> for GenericReceiverVisitor<T>
535where
536    T: for<'a> Deserialize<'a> + Serialize,
537{
538    type Value = GenericReceiver<T>;
539
540    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
541        formatter.write_str("a GenericReceiver variant")
542    }
543
544    fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
545    where
546        A: serde::de::EnumAccess<'de>,
547    {
548        #[derive(Deserialize)]
549        enum GenericReceiverVariantNames {
550            Ipc,
551            Crossbeam,
552        }
553
554        let (variant_name, variant_data): (GenericReceiverVariantNames, _) = data.variant()?;
555
556        match variant_name {
557            GenericReceiverVariantNames::Ipc => variant_data
558                .newtype_variant::<ipc_channel::ipc::IpcReceiver<T>>()
559                .map(|receiver| GenericReceiver(GenericReceiverVariants::Ipc(receiver))),
560            GenericReceiverVariantNames::Crossbeam => {
561                if use_ipc() {
562                    return Err(serde::de::Error::custom(
563                        "Crossbeam channel found in multiprocess mode!",
564                    ));
565                }
566                let addr = variant_data.newtype_variant::<usize>()?;
567                let ptr = addr as *mut RoutedReceiver<T>;
568                // SAFETY: We know we are in the same address space as the sender, so we can safely
569                // reconstruct the Box.
570                #[expect(unsafe_code)]
571                let receiver = unsafe { Box::from_raw(ptr) };
572                Ok(GenericReceiver(GenericReceiverVariants::Crossbeam(
573                    *receiver,
574                )))
575            },
576        }
577    }
578}
579
580impl<'a, T> Deserialize<'a> for GenericReceiver<T>
581where
582    T: for<'de> Deserialize<'de> + Serialize,
583{
584    fn deserialize<D>(d: D) -> Result<GenericReceiver<T>, D::Error>
585    where
586        D: Deserializer<'a>,
587    {
588        d.deserialize_enum(
589            "GenericReceiver",
590            &["Ipc", "Crossbeam"],
591            GenericReceiverVisitor {
592                marker: PhantomData,
593            },
594        )
595    }
596}
597
598/// Private helper function to create a crossbeam based channel.
599///
600/// Do NOT make this function public!
601fn new_generic_channel_crossbeam<T>() -> (GenericSender<T>, GenericReceiver<T>)
602where
603    T: Serialize + for<'de> serde::Deserialize<'de>,
604{
605    let (tx, rx) = crossbeam_channel::unbounded();
606    (
607        GenericSender(GenericSenderVariants::Crossbeam(tx)),
608        GenericReceiver(GenericReceiverVariants::Crossbeam(rx)),
609    )
610}
611
612fn new_generic_channel_ipc<T>() -> Result<(GenericSender<T>, GenericReceiver<T>), std::io::Error>
613where
614    T: Serialize + for<'de> serde::Deserialize<'de>,
615{
616    ipc_channel::ipc::channel().map(|(tx, rx)| {
617        (
618            GenericSender(GenericSenderVariants::Ipc(tx)),
619            GenericReceiver(GenericReceiverVariants::Ipc(rx)),
620        )
621    })
622}
623
624/// Creates a Servo channel that can select different channel implementations based on multiprocess
625/// mode or not. If the scenario doesn't require message to pass process boundary, a simple
626/// crossbeam channel is preferred.
627pub fn channel<T>() -> Option<(GenericSender<T>, GenericReceiver<T>)>
628where
629    T: for<'de> Deserialize<'de> + Serialize,
630{
631    if use_ipc() {
632        new_generic_channel_ipc().ok()
633    } else {
634        Some(new_generic_channel_crossbeam())
635    }
636}
637
638#[cfg(test)]
639mod single_process_channel_tests {
640    //! These unit-tests test that ipc_channel and crossbeam_channel Senders and Receivers
641    //! can be sent over each other without problems in single-process mode.
642    //! In multiprocess mode we exclusively use `ipc_channel` anyway, which is ensured due
643    //! to `channel()` being the only way to construct `GenericSender` and Receiver pairs.
644    use crate::generic_channel::{new_generic_channel_crossbeam, new_generic_channel_ipc};
645
646    #[test]
647    fn generic_crossbeam_can_send() {
648        let (tx, rx) = new_generic_channel_crossbeam();
649        tx.send(5).expect("Send failed");
650        let val = rx.recv().expect("Receive failed");
651        assert_eq!(val, 5);
652    }
653
654    #[test]
655    fn generic_crossbeam_ping_pong() {
656        let (tx, rx) = new_generic_channel_crossbeam();
657        let (tx2, rx2) = new_generic_channel_crossbeam();
658
659        tx.send(tx2).expect("Send failed");
660
661        std::thread::scope(|s| {
662            s.spawn(move || {
663                let reply_sender = rx.recv().expect("Receive failed");
664                reply_sender.send(42).expect("Sending reply failed");
665            });
666        });
667        let res = rx2.recv().expect("Receive of reply failed");
668        assert_eq!(res, 42);
669    }
670
671    #[test]
672    fn generic_ipc_ping_pong() {
673        let (tx, rx) = new_generic_channel_ipc().unwrap();
674        let (tx2, rx2) = new_generic_channel_ipc().unwrap();
675
676        tx.send(tx2).expect("Send failed");
677
678        std::thread::scope(|s| {
679            s.spawn(move || {
680                let reply_sender = rx.recv().expect("Receive failed");
681                reply_sender.send(42).expect("Sending reply failed");
682            });
683        });
684        let res = rx2.recv().expect("Receive of reply failed");
685        assert_eq!(res, 42);
686    }
687
688    #[test]
689    fn send_crossbeam_sender_over_ipc_channel() {
690        let (tx, rx) = new_generic_channel_ipc().unwrap();
691        let (tx2, rx2) = new_generic_channel_crossbeam();
692
693        tx.send(tx2).expect("Send failed");
694
695        std::thread::scope(|s| {
696            s.spawn(move || {
697                let reply_sender = rx.recv().expect("Receive failed");
698                reply_sender.send(42).expect("Sending reply failed");
699            });
700        });
701        let res = rx2.recv().expect("Receive of reply failed");
702        assert_eq!(res, 42);
703    }
704
705    #[test]
706    fn send_generic_ipc_channel_over_crossbeam() {
707        let (tx, rx) = new_generic_channel_crossbeam();
708        let (tx2, rx2) = new_generic_channel_ipc().unwrap();
709
710        tx.send(tx2).expect("Send failed");
711
712        std::thread::scope(|s| {
713            s.spawn(move || {
714                let reply_sender = rx.recv().expect("Receive failed");
715                reply_sender.send(42).expect("Sending reply failed");
716            });
717        });
718        let res = rx2.recv().expect("Receive of reply failed");
719        assert_eq!(res, 42);
720    }
721
722    #[test]
723    fn send_crossbeam_receiver_over_ipc_channel() {
724        let (tx, rx) = new_generic_channel_ipc().unwrap();
725        let (tx2, rx2) = new_generic_channel_crossbeam();
726
727        tx.send(rx2).expect("Send failed");
728        tx2.send(42).expect("Send failed");
729
730        std::thread::scope(|s| {
731            s.spawn(move || {
732                let another_receiver = rx.recv().expect("Receive failed");
733                let res = another_receiver.recv().expect("Receive failed");
734                assert_eq!(res, 42);
735            });
736        });
737    }
738
739    #[test]
740    fn test_timeout_ipc() {
741        let (tx, rx) = new_generic_channel_ipc().unwrap();
742        let timeout_duration = std::time::Duration::from_secs(3);
743        std::thread::spawn(move || {
744            std::thread::sleep(timeout_duration - std::time::Duration::from_secs(1));
745            assert!(tx.send(()).is_ok());
746        });
747        let received = rx.try_recv_timeout(timeout_duration);
748        assert!(received.is_ok());
749    }
750
751    #[test]
752    fn test_timeout_crossbeam() {
753        let (tx, rx) = new_generic_channel_crossbeam();
754        let timeout_duration = std::time::Duration::from_secs(3);
755        std::thread::spawn(move || {
756            std::thread::sleep(timeout_duration - std::time::Duration::from_secs(1));
757            assert!(tx.send(()).is_ok());
758        });
759        let received = rx.try_recv_timeout(timeout_duration);
760        assert!(received.is_ok());
761    }
762}
763
764/// This tests need to be in here because they use the 'new_generic_channel_..' methods
765#[cfg(test)]
766mod generic_receiversets_tests {
767    use std::time::Duration;
768
769    use crate::generic_channel::generic_channelset::{
770        GenericSelectionResult, create_crossbeam_receiver_set, create_ipc_receiver_set,
771    };
772    use crate::generic_channel::{new_generic_channel_crossbeam, new_generic_channel_ipc};
773
774    #[test]
775    fn test_ipc_side1() {
776        let (snd1, recv1) = new_generic_channel_ipc().unwrap();
777        let (snd2, recv2) = new_generic_channel_ipc().unwrap();
778
779        // We keep the senders alive till all threads are done
780        let snd1_c = snd1.clone();
781        let snd2_c = snd2.clone();
782        let mut set = create_ipc_receiver_set();
783        let recv1_select_index = set.add(recv1);
784        let _recv2_select_index = set.add(recv2);
785
786        std::thread::spawn(move || {
787            snd1_c.send(10).unwrap();
788        });
789        std::thread::spawn(move || {
790            std::thread::sleep(Duration::from_secs(1));
791            let _ = snd2_c.send(20); // this might error with closed channel
792        });
793
794        let select_result = set.select();
795        let channel_result = select_result.first().unwrap();
796        assert_eq!(
797            *channel_result,
798            GenericSelectionResult::MessageReceived(recv1_select_index, 10)
799        );
800    }
801
802    #[test]
803    fn test_ipc_side2() {
804        let (snd1, recv1) = new_generic_channel_ipc().unwrap();
805        let (snd2, recv2) = new_generic_channel_ipc().unwrap();
806
807        // We keep the senders alive till all threads are done
808        let snd1_c = snd1.clone();
809        let snd2_c = snd2.clone();
810        let mut set = create_ipc_receiver_set();
811        let _recv1_select_index = set.add(recv1);
812        let recv2_select_index = set.add(recv2);
813
814        std::thread::spawn(move || {
815            std::thread::sleep(Duration::from_secs(1));
816            let _ = snd1_c.send(10);
817        });
818        std::thread::spawn(move || {
819            snd2_c.send(20).unwrap();
820        });
821
822        let select_result = set.select();
823        let channel_result = select_result.first().unwrap();
824        assert_eq!(
825            *channel_result,
826            GenericSelectionResult::MessageReceived(recv2_select_index, 20)
827        );
828    }
829
830    #[test]
831    fn test_crossbeam_side1() {
832        let (snd1, recv1) = new_generic_channel_crossbeam();
833        let (snd2, recv2) = new_generic_channel_crossbeam();
834
835        // We keep the senders alive till all threads are done
836        let snd1_c = snd1.clone();
837        let snd2_c = snd2.clone();
838        let mut set = create_crossbeam_receiver_set();
839        let recv1_select_index = set.add(recv1);
840        let _recv2_select_index = set.add(recv2);
841
842        std::thread::spawn(move || {
843            snd1_c.send(10).unwrap();
844        });
845        std::thread::spawn(move || {
846            std::thread::sleep(Duration::from_secs(2));
847            let _ = snd2_c.send(20);
848        });
849
850        let select_result = set.select();
851        let channel_result = select_result.first().unwrap();
852        assert_eq!(
853            *channel_result,
854            GenericSelectionResult::MessageReceived(recv1_select_index, 10)
855        );
856    }
857
858    #[test]
859    fn test_crossbeam_side2() {
860        let (snd1, recv1) = new_generic_channel_crossbeam();
861        let (snd2, recv2) = new_generic_channel_crossbeam();
862
863        // We keep the senders alive till all threads are done
864        let snd1_c = snd1.clone();
865        let snd2_c = snd2.clone();
866        let mut set = create_crossbeam_receiver_set();
867        let _recv1_select_index = set.add(recv1);
868        let recv2_select_index = set.add(recv2);
869
870        std::thread::spawn(move || {
871            std::thread::sleep(Duration::from_secs(2));
872            let _ = snd1_c.send(10);
873        });
874        std::thread::spawn(move || {
875            snd2_c.send(20).unwrap();
876        });
877
878        let select_result = set.select();
879        let channel_result = select_result.first().unwrap();
880        assert_eq!(
881            *channel_result,
882            GenericSelectionResult::MessageReceived(recv2_select_index, 20)
883        );
884    }
885
886    #[test]
887    fn test_ipc_no_crash_on_disconnect() {
888        // Test that we do not crash if a channel gets disconnected.
889        // Channel 2 gets disconnected because snd2 gets moved into the thread and then falls out of scope
890        let (snd1, recv1) = new_generic_channel_ipc().unwrap();
891        let (snd2, recv2) = new_generic_channel_ipc().unwrap();
892
893        // We keep the senders alive till all threads are done
894        let snd1_c = snd1.clone();
895        let mut set = create_ipc_receiver_set();
896        let _recv1_select_index = set.add(recv1);
897        let recv2_select_index = set.add(recv2);
898
899        std::thread::spawn(move || {
900            std::thread::sleep(Duration::from_secs(2));
901            let _ = snd1_c.send(10);
902        });
903        std::thread::spawn(move || {
904            snd2.send(20).unwrap();
905        });
906        std::thread::sleep(Duration::from_secs(1));
907        let select_result = set.select();
908        let channel_result = select_result.first().unwrap();
909        assert_eq!(
910            *channel_result,
911            GenericSelectionResult::MessageReceived(recv2_select_index, 20)
912        );
913    }
914
915    #[test]
916    fn test_crossbeam_no_crash_on_disconnect() {
917        // Channel 2 gets disconnected because snd2 gets moved into the thread and then falls out of scope
918        let (snd1, recv1) = new_generic_channel_crossbeam();
919        let (snd2, recv2) = new_generic_channel_crossbeam();
920
921        // We keep the senders alive till all threads are done
922        let snd1_c = snd1.clone();
923        let mut set = create_crossbeam_receiver_set();
924        let _recv1_select_index = set.add(recv1);
925        let recv2_select_index = set.add(recv2);
926
927        std::thread::spawn(move || {
928            std::thread::sleep(Duration::from_secs(2));
929            let _ = snd1_c.send(10);
930        });
931        std::thread::spawn(move || {
932            snd2.send(20).unwrap();
933        });
934        std::thread::sleep(Duration::from_secs(1));
935        let select_result = set.select();
936        let channel_result = select_result.first().unwrap();
937        assert_eq!(
938            *channel_result,
939            GenericSelectionResult::MessageReceived(recv2_select_index, 20)
940        );
941    }
942
943    #[test]
944    fn test_ipc_disconnect_correct_message() {
945        // Test that we do not crash if a channel gets disconnected.
946        let (snd1, recv1) = new_generic_channel_ipc().unwrap();
947        let (snd2, recv2) = new_generic_channel_ipc().unwrap();
948
949        // We keep the senders alive till all threads are done
950        let snd1_c = snd1.clone();
951        let mut set = create_ipc_receiver_set();
952        let _recv1_select_index = set.add(recv1);
953        let recv2_select_index = set.add(recv2);
954
955        std::thread::spawn(move || {
956            std::thread::sleep(Duration::from_secs(2));
957            let _ = snd1_c.send(10);
958        });
959        std::thread::spawn(move || {
960            drop(snd2);
961        });
962
963        let select_result = set.select();
964        let channel_result = select_result.first().unwrap();
965        assert_eq!(
966            *channel_result,
967            GenericSelectionResult::ChannelClosed(recv2_select_index)
968        );
969    }
970
971    #[test]
972    fn test_crossbeam_disconnect_correct_messaget() {
973        let (snd1, recv1) = new_generic_channel_crossbeam();
974        let (snd2, recv2) = new_generic_channel_crossbeam();
975
976        // We keep the senders alive till all threads are done
977        let snd1_c = snd1.clone();
978        let mut set = create_crossbeam_receiver_set();
979        let _recv1_select_index = set.add(recv1);
980        let recv2_select_index = set.add(recv2);
981
982        std::thread::spawn(move || {
983            std::thread::sleep(Duration::from_secs(2));
984            let _ = snd1_c.send(10);
985        });
986        std::thread::spawn(move || {
987            drop(snd2);
988        });
989
990        let select_result = set.select();
991        let channel_result = select_result.first().unwrap();
992        assert_eq!(
993            *channel_result,
994            GenericSelectionResult::ChannelClosed(recv2_select_index)
995        );
996    }
997}