llam 0.1.1

Safe, Go-style Rust bindings for the LLAM runtime
use crate::error::{Error, Result};
use crate::sys;
use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
use std::ptr;
use std::time::Duration;

pub struct Mutex<T> {
    raw: *mut sys::llam_mutex_t,
    value: UnsafeCell<T>,
}

unsafe impl<T: Send> Send for Mutex<T> {}
unsafe impl<T: Send> Sync for Mutex<T> {}

impl<T> Mutex<T> {
    pub fn new(value: T) -> Result<Self> {
        let raw = unsafe { sys::llam_mutex_create() };
        if raw.is_null() {
            Err(Error::last())
        } else {
            Ok(Self {
                raw,
                value: UnsafeCell::new(value),
            })
        }
    }

    pub fn lock(&self) -> Result<MutexGuard<'_, T>> {
        let rc = unsafe { sys::llam_mutex_lock(self.raw) };
        if rc == 0 {
            Ok(MutexGuard { mutex: self })
        } else {
            Err(Error::last())
        }
    }

    pub fn lock_timeout(&self, timeout: Duration) -> Result<MutexGuard<'_, T>> {
        self.lock_until(crate::time::deadline_after(timeout))
    }

    pub fn lock_until(&self, deadline_ns: u64) -> Result<MutexGuard<'_, T>> {
        let rc = unsafe { sys::llam_mutex_lock_until(self.raw, deadline_ns) };
        if rc == 0 {
            Ok(MutexGuard { mutex: self })
        } else {
            Err(Error::last())
        }
    }

    pub fn try_lock(&self) -> Result<MutexGuard<'_, T>> {
        let rc = unsafe { sys::llam_mutex_trylock(self.raw) };
        if rc == 0 {
            Ok(MutexGuard { mutex: self })
        } else {
            Err(Error::last())
        }
    }

    fn raw(&self) -> *mut sys::llam_mutex_t {
        self.raw
    }
}

impl<T> Drop for Mutex<T> {
    fn drop(&mut self) {
        if !self.raw.is_null() {
            unsafe {
                let _ = sys::llam_mutex_destroy(self.raw);
            }
            self.raw = ptr::null_mut();
        }
    }
}

pub struct MutexGuard<'a, T> {
    mutex: &'a Mutex<T>,
}

impl<T> Deref for MutexGuard<'_, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.mutex.value.get() }
    }
}

impl<T> DerefMut for MutexGuard<'_, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { &mut *self.mutex.value.get() }
    }
}

impl<T> Drop for MutexGuard<'_, T> {
    fn drop(&mut self) {
        unsafe {
            let _ = sys::llam_mutex_unlock(self.mutex.raw);
        }
    }
}

pub struct Condvar {
    raw: *mut sys::llam_cond_t,
}

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

impl Condvar {
    pub fn new() -> Result<Self> {
        let raw = unsafe { sys::llam_cond_create() };
        if raw.is_null() {
            Err(Error::last())
        } else {
            Ok(Self { raw })
        }
    }

    pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> Result<MutexGuard<'a, T>> {
        let mutex = guard.mutex;
        std::mem::forget(guard);
        let rc = unsafe { sys::llam_cond_wait(self.raw, mutex.raw()) };
        if rc == 0 {
            Ok(MutexGuard { mutex })
        } else {
            unsafe {
                let _ = sys::llam_mutex_unlock(mutex.raw());
            }
            Err(Error::last())
        }
    }

    pub fn wait_timeout<'a, T>(
        &self,
        guard: MutexGuard<'a, T>,
        timeout: Duration,
    ) -> Result<MutexGuard<'a, T>> {
        self.wait_until(guard, crate::time::deadline_after(timeout))
    }

    pub fn wait_until<'a, T>(
        &self,
        guard: MutexGuard<'a, T>,
        deadline_ns: u64,
    ) -> Result<MutexGuard<'a, T>> {
        let mutex = guard.mutex;
        std::mem::forget(guard);
        let rc = unsafe { sys::llam_cond_wait_until(self.raw, mutex.raw(), deadline_ns) };
        if rc == 0 {
            Ok(MutexGuard { mutex })
        } else {
            unsafe {
                let _ = sys::llam_mutex_unlock(mutex.raw());
            }
            Err(Error::last())
        }
    }

    pub fn notify_one(&self) -> Result<()> {
        let rc = unsafe { sys::llam_cond_signal(self.raw) };
        if rc == 0 {
            Ok(())
        } else {
            Err(Error::last())
        }
    }

    pub fn notify_all(&self) -> Result<()> {
        let rc = unsafe { sys::llam_cond_broadcast(self.raw) };
        if rc == 0 {
            Ok(())
        } else {
            Err(Error::last())
        }
    }
}

impl Drop for Condvar {
    fn drop(&mut self) {
        if !self.raw.is_null() {
            unsafe {
                let _ = sys::llam_cond_destroy(self.raw);
            }
            self.raw = ptr::null_mut();
        }
    }
}