Crate smelling_salts[][src]

Getting Started

Add the following to your Cargo.toml.

[dependencies]
smelling_salts = "0.2"
pasts = "0.7"

Example

use smelling_salts::{Device, Watcher};

use std::convert::TryInto;
use std::future::Future;
use std::mem;
use std::os::raw;
use std::pin::Pin;
use std::task::{Context, Poll};

#[allow(non_camel_case_types)]
type c_ssize = isize; // True for most unix
#[allow(non_camel_case_types)]
type c_size = usize; // True for most unix

const MAGIC_NUMBER: u32 = 0xDEAD_BEEF;

// From fcntl.h
const O_CLOEXEC: raw::c_int = 0o2000000;
const O_NONBLOCK: raw::c_int = 0o0004000;
const O_DIRECT: raw::c_int = 0o0040000;

extern "C" {
    fn pipe2(pipefd: *mut [raw::c_int; 2], flags: raw::c_int) -> raw::c_int;
    fn write(fd: raw::c_int, buf: *const raw::c_void, count: c_size) -> c_ssize;
    fn read(fd: raw::c_int, buf: *mut raw::c_void, count: c_size) -> c_ssize;
    fn close(fd: raw::c_int) -> raw::c_int;
}

// Convert a C error (negative on error) into a result.
fn error(err: raw::c_int) -> Result<(), raw::c_int> {
    if err < 0 {
        Err(err)
    } else {
        Ok(())
    }
}

fn fd_close(fd: raw::c_int) {
    // close() should never fail.
    let ret = unsafe { close(fd) };
    error(ret).unwrap();
}

// Create the sender and receiver for a pipe.
fn new_pipe() -> (raw::c_int, raw::c_int) {
    let [recver, sender] = unsafe {
        // Create pipe for communication
        let mut pipe = mem::MaybeUninit::<[raw::c_int; 2]>::uninit();
        error(pipe2(pipe.as_mut_ptr(), O_CLOEXEC | O_NONBLOCK | O_DIRECT)).unwrap();
        pipe.assume_init()
    };

    (sender, recver)
}

fn write_u32(fd: raw::c_int, data: u32) {
    let data = [data];
    let len: usize = unsafe {
        write(fd, data.as_ptr().cast(), mem::size_of::<u32>())
            .try_into()
            .unwrap()
    };
    assert_eq!(len, mem::size_of::<u32>());
}

fn read_u32(fd: raw::c_int) -> Option<u32> {
    let ret = unsafe {
        let mut buffer = mem::MaybeUninit::<u32>::uninit();
        let len: usize = read(fd, buffer.as_mut_ptr().cast(), mem::size_of::<u32>())
            .try_into()
            .unwrap_or(0);
        if len == 0 {
            return None;
        }
        assert_eq!(len, mem::size_of::<u32>());
        buffer.assume_init()
    };
    Some(ret)
}

pub struct PipeReceiver(Device);

impl PipeReceiver {
    pub fn new(fd: raw::c_int) -> Self {
        PipeReceiver(Device::new(fd, Watcher::new().input()))
    }
}

impl Drop for PipeReceiver {
    fn drop(&mut self) {
        // Deregister FD, then delete (must be in this order).
        self.0.old();
        fd_close(self.0.raw());
    }
}

pub struct PipeFuture<'a>(&'a PipeReceiver);

impl<'a> PipeFuture<'a> {
    pub fn new(recver: &'a PipeReceiver) -> Self {
        PipeFuture(recver)
    }
}

impl<'a> Future for PipeFuture<'a> {
    type Output = u32;

    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        if let Some(output) = read_u32((self.0).0.raw()) {
            Poll::Ready(output)
        } else {
            (self.0).0.register_waker(cx.waker());
            Poll::Pending
        }
    }
}

async fn async_main() {
    let (sender, recver) = new_pipe();
    let device = PipeReceiver::new(recver);

    std::thread::spawn(move || {
        std::thread::sleep(std::time::Duration::from_millis(1000));
        write_u32(sender, MAGIC_NUMBER);
        fd_close(sender);
    });

    let output = PipeFuture::new(&device).await;
    assert_eq!(output, MAGIC_NUMBER);
}

fn main() {
    pasts::block_on(async_main());
}

Structs

Device

Represents some device.

Watcher

Which events to watch for to trigger a wake-up.

Type Definitions

RawDevice

On Linux, RawDevice corresponds to RawFd