rt 0.19.1

A real-time operating system capable of full preemption
Documentation
use core::{
    cell::UnsafeCell,
    marker::PhantomData,
    ops::{Deref, DerefMut},
    panic::{RefUnwindSafe, UnwindSafe},
};

use crate::{
    bindings::{
        rt_mutex, rt_rwlock, rt_rwlock_rdlock, rt_rwlock_rdunlock, rt_rwlock_timedrdlock,
        rt_rwlock_timedwrlock, rt_rwlock_tryrdlock, rt_rwlock_trywrlock, rt_rwlock_wrlock,
        rt_rwlock_wrunlock,
    },
    list::list_init,
    ptr_macros::{ptr_to_field, ptr_to_field_mut},
    sync::condvar::c_cond_init,
    tick::Utick,
};

pub struct RwLock<T: ?Sized> {
    rwlock: UnsafeCell<rt_rwlock>,
    data: UnsafeCell<T>,
}

unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
impl<T: ?Sized> UnwindSafe for RwLock<T> {}
impl<T: ?Sized> RefUnwindSafe for RwLock<T> {}

impl<T> RwLock<T> {
    /// Initialize a new `RwLock` with the given data.
    ///
    /// # Safety
    ///
    /// This must be called with a pointer to a `static` `RwLock` and be used to initialize that
    /// same `RwLock`. Users should use the `rt::sync::rwlock!` macro to create a `RwLock` rather than
    /// call `RwLock::init` directly.
    #[must_use]
    pub const unsafe fn init(this: *const Self, t: T) -> RwLock<T> {
        let rwlock = UnsafeCell::raw_get(ptr_to_field!(this, rwlock));
        RwLock {
            rwlock: UnsafeCell::new(rt_rwlock {
                mutex: rt_mutex {
                    holder: 0,
                    wait_list: list_init(ptr_to_field_mut!(rwlock, mutex, wait_list)),
                    list: list_init(ptr_to_field_mut!(rwlock, mutex, list)),
                    level: -1,
                },
                cond: c_cond_init(ptr_to_field_mut!(rwlock, cond)),
                value: 0,
            }),
            data: UnsafeCell::new(t),
        }
    }
}

impl<T: ?Sized> RwLock<T> {
    #[inline]
    pub fn read(&self) -> RwLockReadGuard<'_, T> {
        unsafe { rt_rwlock_rdlock(self.rwlock.get()) }
        RwLockReadGuard::new(self)
    }

    #[inline]
    pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>> {
        if unsafe { rt_rwlock_tryrdlock(self.rwlock.get()) } {
            Some(RwLockReadGuard::new(self))
        } else {
            None
        }
    }

    #[inline]
    pub fn timed_read(&self, ticks: Utick) -> Option<RwLockReadGuard<'_, T>> {
        if unsafe { rt_rwlock_timedrdlock(self.rwlock.get(), ticks) } {
            Some(RwLockReadGuard::new(self))
        } else {
            None
        }
    }

    #[inline]
    pub fn write(&self) -> RwLockWriteGuard<'_, T> {
        unsafe { rt_rwlock_wrlock(self.rwlock.get()) }
        RwLockWriteGuard::new(self)
    }

    #[inline]
    pub fn try_write(&self) -> Option<RwLockWriteGuard<'_, T>> {
        if unsafe { rt_rwlock_trywrlock(self.rwlock.get()) } {
            Some(RwLockWriteGuard::new(self))
        } else {
            None
        }
    }

    #[inline]
    pub fn timed_write(&self, ticks: Utick) -> Option<RwLockWriteGuard<'_, T>> {
        if unsafe { rt_rwlock_timedwrlock(self.rwlock.get(), ticks) } {
            Some(RwLockWriteGuard::new(self))
        } else {
            None
        }
    }

    // Probably not useful because this RwLock must be static.
    #[inline]
    pub fn get_mut(&mut self) -> &mut T {
        self.data.get_mut()
    }
}

pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
    lock: &'a RwLock<T>,
    _unsend_marker: PhantomData<*const ()>,
}

impl<T: ?Sized> RwLockReadGuard<'_, T> {
    #[inline]
    const fn new(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
        RwLockReadGuard {
            lock,
            _unsend_marker: PhantomData,
        }
    }

    // Use `unlock` only if you need to explicitly release the RwLock associated with the
    // RwLockReadGuard before it would go out of scope and unlock itself.
    #[inline]
    pub fn unlock(self) {
        drop(self)
    }
}

unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {}

impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &T {
        unsafe { &*self.lock.data.get() }
    }
}

impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> {
    #[inline]
    fn drop(&mut self) {
        unsafe { rt_rwlock_rdunlock(self.lock.rwlock.get()) }
    }
}

pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
    lock: &'a RwLock<T>,
    _unsend_marker: PhantomData<*const ()>,
}

impl<T: ?Sized> RwLockWriteGuard<'_, T> {
    #[inline]
    const fn new(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
        RwLockWriteGuard {
            lock,
            _unsend_marker: PhantomData,
        }
    }

    // Use `unlock` only if you need to explicitly release the RwLock associated with the
    // RwLockWriteGuard before it would go out of scope and unlock itself.
    #[inline]
    pub fn unlock(self) {
        drop(self)
    }
}

unsafe impl<T: ?Sized + Sync> Sync for RwLockWriteGuard<'_, T> {}

impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &T {
        unsafe { &*self.lock.data.get() }
    }
}

impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.lock.data.get() }
    }
}

impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> {
    #[inline]
    fn drop(&mut self) {
        unsafe { rt_rwlock_wrunlock(self.lock.rwlock.get()) }
    }
}

#[macro_export]
macro_rules! rwlock {
    ($name: ident, $type: ty, $data: expr) => {
        static $name: $crate::sync::RwLock<$type> = {
            let ptr = &raw const $name;
            let data = $data;
            unsafe { $crate::sync::RwLock::init(ptr, data) }
        };
    };
}

#[cfg(test)]
mod tests {
    use crate::test_init;

    #[test]
    fn fast_path() {
        test_init();
        rwlock!(LOCK, i32, 0);
        *LOCK.write() += 1;
        assert_eq!(*LOCK.read(), 1);
    }
}