Search code examples
rustunix-socket

"Resource temporarily unavailable" on non-blocking Unix datagram read


I am writing a process to move data back and forth between instances of itself.

I am receiving the error:

thread 'main' panicked at 'called Result::unwrap() on an Err value: Os { code: 11, kind: WouldBlock, message: "Resource temporarily unavailable" }', server/src/main.rs:44:37

My current implementation:

#![feature(duration_constants)]
use std::os::unix::net::UnixDatagram;
use std::time::Duration;

const OLD_PROCESS: &str = "./old";
const NEW_PROCESS: &str = "./new";

fn startup() {
    // We send the shutdown notification (the data here doesnt matter) then receive the data.
    let socket = UnixDatagram::bind(NEW_PROCESS).unwrap();

    println!("sending shutdown signal");
    socket.send_to(&[1], OLD_PROCESS).unwrap();
    println!("sent shutdown signal");

    println!("reciving data");
    let mut data = [Default::default(); 4];
    socket.recv(&mut data).unwrap();
    println!("data: {:?}", data);

    std::fs::remove_file(NEW_PROCESS).unwrap();
}
fn process() {
    // Once we receive something (indicating a new process has started) we then transfer data.
    let socket = UnixDatagram::bind(OLD_PROCESS).unwrap();

    println!("awaiting shutdown signal");

    // For reading the shutdown signal, we set the socket to non-blocking (like `Mutex::try_read`).
    let mut notif_buf = [0; 1];

    // It seems by setting nonblocking and the read timeout causes the error:
    // ```
    // thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 11, kind: WouldBlock, message: "Resource temporarily unavailable" }', server/src/main.rs:44:37
    // ```
    socket.set_nonblocking(true).unwrap();
    socket
        .set_read_timeout(Some(Duration::MICROSECOND))
        .unwrap();
    println!("socket.read_timeout(): {:?}", socket.read_timeout());

    // Our work loop
    while notif_buf[0] == 0 {
        // Some other work
        {
            print!(".");
            std::thread::sleep(Duration::MILLISECOND);
        }
        // Check if recieved shutdown signal
        socket.recv(&mut notif_buf).unwrap();
    }
    println!();
    println!("recived shutdown signal");
    socket.set_nonblocking(false).unwrap();

    println!("sending data");
    socket.send_to(&[1, 2, 3, 4], NEW_PROCESS).unwrap();
    println!("sent data");

    std::fs::remove_file(OLD_PROCESS).unwrap();
}

fn main() {
    println!("Hello, world!");

    // The following processes use it
    if std::path::Path::new(OLD_PROCESS).exists() {
        println!("non-first");
        startup();
        process();
    }
    // The first process creates the socket
    else {
        println!("first");
        process();
    }
}

Notably with this you will need to delete the old file descriptor at the end of each test manually for now

In the simplest sense I want a loop with UnixDatagram like (with try_read like Mutex::try_read):

loop {
    // Some work ...
    match socket.try_read() {
        Some(_) => break,
        None => continue
    }
}

Solution

  • When using:

    socket.set_nonblocking(true).unwrap();
    socket.set_read_timeout(Some(Duration::MICROSECOND)).unwrap();
    

    it seems when the timeout occurs recv returns Err thus by changing the code:

    // Check if recieved shutdown signal
    socket.recv(&mut notif_buf).unwrap();
    

    to

    match socket.recv(&mut notif_buf) {
        Ok(_) => break,
        Err(_) if err.kind() == ErrorKind::WouldBlock => continue,
        _ => unreachable!()
    }
    

    this fixes the issue.