unix_ipc/
bootstrap.rs

1use serde_::de::DeserializeOwned;
2use serde_::Serialize;
3use std::cell::RefCell;
4use std::fs;
5use std::io;
6use std::os::unix::io::{FromRawFd, IntoRawFd};
7use std::os::unix::net::UnixListener;
8use std::path::{Path, PathBuf};
9
10use crate::typed_channel::Sender;
11
12/// A bootstrap helper.
13///
14/// This creates a unix socket that is linked to the file system so
15/// that a [`Receiver`](struct.Receiver.html) can connect to it.  It
16/// lets you send one or more messages to the connected receiver.
17#[derive(Debug)]
18pub struct Bootstrapper<T> {
19    listener: UnixListener,
20    sender: RefCell<Option<Sender<T>>>,
21    path: PathBuf,
22}
23
24impl<T: Serialize + DeserializeOwned> Bootstrapper<T> {
25    /// Creates a bootstrapper at a random socket in `/tmp`.
26    #[cfg(feature = "bootstrap-simple")]
27    pub fn new() -> io::Result<Bootstrapper<T>> {
28        use rand::{thread_rng, RngCore};
29        use std::time::{SystemTime, UNIX_EPOCH};
30
31        let mut dir = std::env::temp_dir();
32        let mut rng = thread_rng();
33        let now = SystemTime::now();
34        dir.push(&format!(
35            ".rust-unix-ipc.{}-{}.sock",
36            now.duration_since(UNIX_EPOCH).unwrap().as_secs(),
37            rng.next_u64(),
38        ));
39        Bootstrapper::bind(&dir)
40    }
41
42    /// Creates a bootstrapper at a specific socket path.
43    pub fn bind<P: AsRef<Path>>(p: P) -> io::Result<Bootstrapper<T>> {
44        fs::remove_file(&p).ok();
45        let listener = UnixListener::bind(&p)?;
46        Ok(Bootstrapper {
47            listener,
48            sender: RefCell::new(None),
49            path: p.as_ref().to_path_buf(),
50        })
51    }
52
53    /// Returns the path of the socket.
54    pub fn path(&self) -> &Path {
55        &self.path
56    }
57
58    /// Consumes the boostrapper and sends a single value in.
59    ///
60    /// This can be called multiple times to send more than one value
61    /// into the inner socket.
62    pub fn send(&self, val: T) -> io::Result<()> {
63        if self.sender.borrow().is_none() {
64            let (sock, _) = self.listener.accept()?;
65            let sender = unsafe { Sender::from_raw_fd(sock.into_raw_fd()) };
66            *self.sender.borrow_mut() = Some(sender);
67        }
68        self.sender.borrow().as_ref().unwrap().send(val)
69    }
70}
71
72impl<T> Drop for Bootstrapper<T> {
73    fn drop(&mut self) {
74        fs::remove_file(&self.path).ok();
75    }
76}
77
78#[test]
79fn test_bootstrap() {
80    use crate::Receiver;
81
82    let bootstrapper = Bootstrapper::new().unwrap();
83    let path = bootstrapper.path().to_owned();
84
85    let handle = std::thread::spawn(move || {
86        let receiver = Receiver::<u32>::connect(path).unwrap();
87        let a = receiver.recv().unwrap();
88        let b = receiver.recv().unwrap();
89        assert_eq!(a + b, 65);
90    });
91
92    bootstrapper.send(42u32).unwrap();
93    bootstrapper.send(23u32).unwrap();
94
95    handle.join().unwrap();
96}
97
98#[test]
99fn test_bootstrap_reverse() {
100    use crate::{channel, Receiver};
101
102    let bootstrapper = Bootstrapper::new().unwrap();
103    let path = bootstrapper.path().to_owned();
104    let (tx, rx) = channel::<u32>().unwrap();
105
106    std::thread::spawn(move || {
107        let receiver = Receiver::<Sender<u32>>::connect(path).unwrap();
108        let result_sender = receiver.recv().unwrap();
109        result_sender.send(42 + 23).unwrap();
110    });
111
112    bootstrapper.send(tx).unwrap();
113    assert_eq!(rx.recv().unwrap(), 65);
114}