lowlet 0.1.2

Low-latency IPC library using shared memory and lock-free structures
Documentation
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

use crate::buf::RingBuffer;
use crate::err::{Error, Result};

pub struct Sender<T, const N: usize> {
    buf: Arc<RingBuffer<T, N>>,
    closed: Arc<AtomicBool>,
}

impl<T, const N: usize> Sender<T, N> {
    #[allow(dead_code)]
    pub(crate) fn new(buf: Arc<RingBuffer<T, N>>) -> Self {
        Self {
            buf,
            closed: Arc::new(AtomicBool::new(false)),
        }
    }

    pub(crate) fn new_with_closed(buf: Arc<RingBuffer<T, N>>, closed: Arc<AtomicBool>) -> Self {
        Self { buf, closed }
    }

    #[allow(dead_code)]
    pub(crate) fn closed_flag(&self) -> Arc<AtomicBool> {
        self.closed.clone()
    }

    #[inline]
    pub fn send(&self, value: T) -> Result<()> {
        if self.closed.load(Ordering::Acquire) {
            return Err(Error::ChannelClosed);
        }
        self.buf.send(value)
    }

    #[inline]
    pub fn try_send(&self, value: T) -> Result<()> {
        self.send(value)
    }

    pub fn send_spin(&self, value: T) -> Result<()>
    where
        T: Copy,
    {
        loop {
            if self.closed.load(Ordering::Acquire) {
                return Err(Error::ChannelClosed);
            }
            match self.buf.send(value) {
                Ok(()) => return Ok(()),
                Err(Error::QueueFull) => std::hint::spin_loop(),
                Err(e) => return Err(e),
            }
        }
    }

    #[inline]
    pub fn capacity(&self) -> usize {
        N
    }

    #[inline]
    pub fn is_full(&self) -> bool {
        self.buf.len() >= N
    }

    #[inline]
    pub fn is_closed(&self) -> bool {
        self.closed.load(Ordering::Acquire)
    }

    pub fn close(&self) {
        self.closed.store(true, Ordering::Release);
    }

    pub fn send_batch(&self, items: &[T]) -> Result<usize>
    where
        T: Copy,
    {
        if self.closed.load(Ordering::Acquire) {
            return Err(Error::ChannelClosed);
        }

        let mut sent = 0;

        for item in items {
            match self.buf.send(*item) {
                Ok(()) => sent += 1,
                Err(e) => {
                    if sent == 0 {
                        return Err(e);
                    }
                    break;
                }
            }
        }

        Ok(sent)
    }

    pub fn send_timeout(&self, value: T, timeout: std::time::Duration) -> Result<()>
    where
        T: Copy,
    {
        let start = std::time::Instant::now();

        loop {
            if self.closed.load(Ordering::Acquire) {
                return Err(Error::ChannelClosed);
            }
            match self.buf.send(value) {
                Ok(()) => return Ok(()),
                Err(e) => {
                    if start.elapsed() >= timeout {
                        return Err(e);
                    }
                    std::hint::spin_loop();
                }
            }
        }
    }
}

impl<T, const N: usize> Clone for Sender<T, N> {
    fn clone(&self) -> Self {
        Self {
            buf: self.buf.clone(),
            closed: self.closed.clone(),
        }
    }
}

unsafe impl<T: Send, const N: usize> Send for Sender<T, N> {}
unsafe impl<T: Send, const N: usize> Sync for Sender<T, N> {}