rt 0.19.0

A real-time operating system capable of full preemption
Documentation
use core::{
    cell::UnsafeCell,
    mem::forget,
    panic::{RefUnwindSafe, UnwindSafe},
};

use crate::{
    bindings::{rt_cond, rt_cond_broadcast, rt_cond_signal, rt_cond_timedwait, rt_cond_wait},
    ptr_macros::{ptr_to_field, ptr_to_field_mut},
    sync::{MutexGuard, sem::c_sem_init},
    tick::Utick,
};

#[repr(transparent)]
pub struct Condvar {
    cond: UnsafeCell<rt_cond>,
}

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

pub(crate) const fn c_cond_init(c: *mut rt_cond) -> rt_cond {
    rt_cond {
        sem: c_sem_init(ptr_to_field_mut!(c, sem), 0, 1),
    }
}

impl Condvar {
    /// Initialize a new `Condvar`.
    ///
    /// # Safety
    ///
    /// This must be called with a pointer to a `static` `Condvar` and be used to initialize that
    /// same `Condvar`. Users should use the `rt::sync::condvar!` macro to create a `Condvar`
    /// rather than call `Condvar::init` directly.
    #[must_use]
    pub const unsafe fn init(this: *const Self) -> Condvar {
        let cond = UnsafeCell::raw_get(ptr_to_field!(this, cond));
        Condvar {
            cond: UnsafeCell::new(c_cond_init(cond)),
        }
    }

    /// Wake at most one task that is waiting on the `Condvar`.
    #[inline]
    pub fn signal(&self) {
        unsafe { rt_cond_signal(self.cond.get()) }
    }

    /// Wake all tasks that are waiting on the `Condvar`.
    #[inline]
    pub fn broadcast(&self) {
        unsafe { rt_cond_broadcast(self.cond.get()) }
    }

    /// Alias for `Condvar::signal` to match `std::sync::Condvar`.
    #[inline]
    pub fn notify_one(&self) {
        self.signal()
    }

    /// Alias for `Condvar::broadcast` to match `std::sync::Condvar`.
    #[inline]
    pub fn notify_all(&self) {
        self.broadcast()
    }

    /// Unlock the mutex until the `Condvar` has been signaled, and re-acquire the mutex afterward.
    #[inline]
    pub fn wait<T>(&self, guard: &MutexGuard<T>) {
        unsafe { rt_cond_wait(self.cond.get(), guard.ptr()) }
    }

    /// Repeatedly `wait` on the `Condvar` while `condition` returns `true`.
    pub fn wait_while<T, F>(&self, guard: &MutexGuard<T>, condition: F)
    where
        F: Fn(&T) -> bool,
    {
        while condition(guard) {
            self.wait(guard);
        }
    }

    /// `wait_while` but allow mutation of the guard and other state from the `condition` function.
    pub fn wait_while_mut<T, F>(&self, guard: &mut MutexGuard<T>, mut condition: F)
    where
        F: FnMut(&mut T) -> bool,
    {
        while condition(guard) {
            self.wait(guard);
        }
    }

    #[inline]
    pub fn timed_wait<'a, T>(
        &self,
        guard: MutexGuard<'a, T>,
        ticks: Utick,
    ) -> Option<MutexGuard<'a, T>> {
        if unsafe { rt_cond_timedwait(self.cond.get(), guard.ptr(), ticks) } {
            Some(guard)
        } else {
            // The mutex was already unlocked inside of rt_cond_timedwait and only re-locked if
            // successful. On failure, we don't return the guard, but we must also avoid dropping
            // it, which would unlock the mutex again.
            forget(guard);
            None
        }
    }
}

#[macro_export]
macro_rules! condvar {
    ($name: ident) => {
        static $name: $crate::sync::Condvar = {
            let ptr = &raw const $name;
            unsafe { $crate::sync::Condvar::init(ptr) }
        };
    };
}

#[cfg(test)]
mod tests {
    #[test]
    fn signal() {
        condvar!(CONDVAR);
        CONDVAR.signal();
    }
}