3
\$\begingroup\$

This program

  1. Detects application uniqueness, if the application is a unique/primary instance, it launches a server, otherwise a client over a Unix domain socket.

  2. Client will send a message that will trigger the server to invoke a time-heavy task.

  3. When the task is running, the server will ignore all the incoming messages. This is currently worked-around by making the client send the current time to the server. Server keeps track of the heavy task and when it finishes. I'd like a way to actually drop the socket before the task starts but I can't find a way to re-activate the socket.

///// Begin imports
use {
    nix::{
        errno::Errno::EADDRINUSE,
        sys::socket::{
            bind, connect, recv, send, socket, AddressFamily, MsgFlags, SockAddr, SockFlag,
            SockType, UnixAddr,
        },
        unistd::close,
        Error::Sys,
    }, // "0.15.0"
    std::{error::Error, os::unix::io::RawFd},
};
///// End imports
///// Begin constsnts
static SOCK_ADDR: &'static str = "com.localserver.myapp.sock";
// message capacity is the size
// of the bytes that represent a u64
// can't create static size array
const MESSAGE_CAPACITY: usize = 64 / 8;

//// End constants

//// Begin macros
macro_rules! Expected {
    () => {
        Result<(), Box<dyn Error>>
    };
    ($t:ty) => {
        Result<$t, Box<dyn Error>>
    }
}
//// End Macros

//// Begin helper functions
fn heavy_task() -> Expected!() {
    println!("Performing heavy task");
    std::thread::sleep(std::time::Duration::from_secs(5));
    println!("Task finished.");
    Ok(())
}
/// Return the current timestamp in nanosecond precision
fn get_time() -> Expected!(u64) {
    Ok(std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)?
        .as_nanos() as u64)
}
//// End helper functions
fn server(msgsock: RawFd) -> Expected!() {
    let mut earliest_non_busy_time = get_time()?;

    loop {
        println!("Listening...");
        loop {
            let mut buf = [0u8; MESSAGE_CAPACITY];
            let rval = recv(msgsock, &mut buf[..], MsgFlags::empty())?;
            if rval != MESSAGE_CAPACITY {
                break;
            } else {
                let incoming_time = u64::from_be_bytes(buf);
                if incoming_time > earliest_non_busy_time {
                    heavy_task()?;
                    earliest_non_busy_time = get_time()?;
                } else {
                    println!("Task request rejected since the task had already been running");
                }
            }
        }
    }
    // Ok(()) // unreachable
}
fn client(sock: RawFd, addr: &SockAddr) -> Expected!() {
    match connect(sock, addr) {
        Ok(_) => {
            let current_time = get_time()?;
            let msg = current_time.to_be_bytes();
            if send(sock, &msg, MsgFlags::empty())? != MESSAGE_CAPACITY {
                close(sock)?;
                panic!("Message could not be sent");
            }
            close(sock)?;
            Ok(())
        }
        Err(e) => {
            close(sock)?;
            panic!("Error connecting to socket: {}", e);
        }
    }
}
fn main() -> Expected!() {
    let sock: RawFd = socket(
        AddressFamily::Unix,
        SockType::Datagram,
        SockFlag::empty(),
        None, // Protocol
    )?;

    let addr = SockAddr::Unix(UnixAddr::new_abstract(SOCK_ADDR.as_bytes())?);

    match bind(sock, &addr) {
        Err(e) => match e {
            Sys(EADDRINUSE) => {
                println!("Secondary instance detected, launching client.");
                match client(sock, &addr) {
                    Ok(_) => println!("Message sent."),
                    Err(_) => println!("Message sending failed."),
                }
                println!("Exiting...");
            }
            _ => {
                panic!("Socket binding failed due to: {:?}", e);
            }
        },
        Ok(_) => {
            println!("Primary instance detected, launching server.");
            server(sock)?;
        }
    }
    Ok(())
}

Further explained here: https://gitlab.com/snippets/1903637

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.