global_channel/
lib.rs

1pub extern crate crossbeam_channel;
2pub use paste;
3pub use sync_unsafe_cell;
4
5/// Creates a new global/static channel with the given name, bounds, and item type.
6///
7/// # Example
8///
9/// ```rust
10/// use global_channel::global_channel;
11///
12/// global_channel!(my_channel, None, u32);
13///
14/// fn main() {
15///     let tx = my_channel_tx();
16///     let rx = my_channel_rx();
17///     tx.send(42).unwrap();
18///     assert_eq!(rx.recv().unwrap(), 42);
19/// }
20#[macro_export]
21macro_rules! global_channel {
22    ($channel_name:ident, $bounds:expr, $item:ty) => {
23        $crate::paste::paste! {
24            #[allow(non_upper_case_globals)]
25            static [<__ $channel_name _TX>]: $crate::sync_unsafe_cell::SyncUnsafeCell<
26                ::std::mem::MaybeUninit<$crate::crossbeam_channel::Sender<$item>>
27            > = $crate::sync_unsafe_cell::SyncUnsafeCell::new(::std::mem::MaybeUninit::uninit());
28
29            #[allow(non_upper_case_globals)]
30            static [<__ $channel_name _RX>]: $crate::sync_unsafe_cell::SyncUnsafeCell<
31                ::std::mem::MaybeUninit<$crate::crossbeam_channel::Receiver<$item>>
32            > = $crate::sync_unsafe_cell::SyncUnsafeCell::new(::std::mem::MaybeUninit::uninit());
33
34            // Initializes the channel once per process
35            #[allow(non_snake_case)]
36            fn [<setup_channel_ $channel_name>](bounds: Option<usize>) {
37                static INIT: ::std::sync::Once = ::std::sync::Once::new();
38                INIT.call_once(|| {
39                    let (tx, rx) = match bounds {
40                        Some(b) => $crate::crossbeam_channel::bounded(b),
41                        None => $crate::crossbeam_channel::unbounded(),
42                    };
43                    unsafe {
44                        let tx_ref = &mut *[<__ $channel_name _TX>].get();
45                        let rx_ref = &mut *[<__ $channel_name _RX>].get();
46                        tx_ref.write(tx);
47                        rx_ref.write(rx);
48                    }
49                });
50            }
51
52            #[allow(non_snake_case)]
53            #[inline]
54            pub fn [<$channel_name _tx>]() -> &'static $crate::crossbeam_channel::Sender<$item> {
55                [<setup_channel_ $channel_name>]($bounds);
56                unsafe { &*(*[<__ $channel_name _TX>].get()).as_ptr() }
57            }
58
59            #[allow(non_snake_case)]
60            #[inline]
61            pub fn [<$channel_name _rx>]() -> &'static $crate::crossbeam_channel::Receiver<$item> {
62                [<setup_channel_ $channel_name>]($bounds);
63                unsafe { &*(*[<__ $channel_name _RX>].get()).as_ptr() }
64            }
65        }
66    };
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    global_channel!(test_channel, None, u32);
74
75    #[test]
76    fn test_basic_usage() {
77        let tx = test_channel_tx();
78        let rx = test_channel_rx();
79        tx.send(42).unwrap();
80        assert_eq!(rx.recv().unwrap(), 42);
81    }
82}