rt 0.19.1

A real-time operating system capable of full preemption
Documentation
use core::sync::atomic::{AtomicI32, Ordering};

use crate::{ptr_macros::ptr_to_field, sync::Mutex};

pub struct Once {
    done: AtomicI32,
    mutex: Mutex<()>,
}

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

    /// Call `f` exactly once among all callers of `call_once(f)`. If a `call_once(f)` is in
    /// progress, other callers will block until `f` has returned.
    pub fn call_once<F: FnOnce()>(&self, f: F) {
        if self.done.load(Ordering::Acquire) == 0 {
            let _guard = self.mutex.lock();
            if self.done.load(Ordering::Relaxed) == 0 {
                f();
                self.done.store(1, Ordering::Release);
            }
        }
    }

    pub fn is_completed(&self) -> bool {
        self.done.load(Ordering::Acquire) != 0
    }
}

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

#[cfg(test)]
mod tests {
    #[test]
    fn fast_path() {
        let mut x = 0i32;
        once!(ONCE);
        ONCE.call_once(|| {
            x += 1;
        });
        assert!(ONCE.is_completed());
        ONCE.call_once(|| {
            x += 1;
        });
        assert_eq!(x, 1);
    }
}