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