cogo 0.1.36

Rust Coroutine Library like go
Documentation
use std::sync::atomic::{AtomicU32, Ordering};
use crate::defer;
use crate::std::sync::Mutex;

/// Once is an object that will perform exactly one action.
///
/// A Once must not be copied after first use.
pub struct Once {
    done: AtomicU32,
}

impl Once {
    pub fn new() -> Self {
        Self {
            done: Default::default(),
        }
    }
    /// Do calls the function f if and only if Do is being called for the
    /// first time for this instance of Once. In other words, given
    /// 	var once Once
    /// if once.Do(f) is called multiple times, only the first call will invoke f,
    /// even if f has a different value in each invocation. A new instance of
    /// Once is required for each function to execute.
    ///
    /// Do is intended for initialization that must be run exactly once. Since f
    /// is niladic, it may be necessary to use a function literal to capture the
    /// arguments to a function to be invoked by Do:
    /// 	config.once.do(|| { config.init(filename) })
    ///
    /// Because no call to Do returns until the one call to f returns, if f causes
    /// Do to be called, it will deadlock.
    ///
    /// If f panics, Do considers it to have returned; future calls of Do return
    /// without calling f.
    pub fn r#do<F>(&self, f: F) where F: FnMut() {
        if self.done.load(Ordering::SeqCst) == 0 {
            self.do_slow(f);
        }
    }

    fn do_slow<F>(&self, mut f: F) where F: FnMut() {
        if self.done.load(Ordering::SeqCst) == 0 {
            self.done.store(1, Ordering::SeqCst);
            f();
        }
    }
}

#[cfg(test)]
mod test {
    use std::cell::UnsafeCell;
    use std::panic::catch_unwind;
    use std::sync::Arc;
    use crate::{chan, defer};
    use crate::std::sync::channel::Sender;
    use crate::std::sync::Once;

    pub struct One {
        pub inner: UnsafeCell<i32>,
    }

    unsafe impl Send for One {}

    unsafe impl Sync for One {}

    impl One {
        pub fn increment(&self) {
            *(unsafe { &mut *self.inner.get() }) += 1;
        }
        pub fn value(&self) -> i32 {
            unsafe {
                *self.inner.get()
            }
        }
    }


    unsafe fn run(once: Arc<Once>, o: Arc<One>, s: Arc<Sender<bool>>) {
        once.r#do(|| {
            o.increment();
        });
        if o.value() != 1 {
            panic!(format!("once failed inside run: {} is not 1", o.value()));
        }
        s.send(true);
    }

    #[test]
    fn test_once() {
        let one = Arc::new(One { inner: UnsafeCell::new(0) });
        let once = Arc::new(Once::new());
        let (s, r) = chan!();
        let sender = Arc::new(s);
        let n = 10;
        for i in 0..n {
            let oc = once.clone();
            let s = sender.clone();
            let o = one.clone();
            go!(move ||{unsafe {run(oc,o,s);}});
        }
        for i in 0..n {
            r.recv();
        }
        if one.value() != 1 {
            panic!(format!("once failed outside run: {} is not 1", one.value()));
        }
    }

    #[test]
    fn test_once_panic() {
        let once = Once::new();
        catch_unwind(|| {
            once.r#do(|| {
                panic!("failed");
            });
        });
        once.r#do(|| {
            panic!("Once.Do called twice");
        });
    }
}