#![warn(missing_docs, missing_debug_implementations, unreachable_pub)]
#![cfg_attr(not(test), no_std)]
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
mod arc_waker;
mod borrowed_waker;
mod loom_exports;
#[deprecated(
    since = "0.2.0",
    note = "items from this module are now available in the root module"
)]
pub mod primitives;
mod waker;
#[cfg(feature = "alloc")]
pub use arc_waker::{WakeSink, WakeSource};
pub use borrowed_waker::{WakeSinkRef, WakeSourceRef};
pub use waker::{DiatomicWaker, WaitUntil};
#[cfg(all(test, not(diatomic_waker_loom)))]
mod tests {
    use std::sync::atomic::{AtomicBool, Ordering};
    use std::thread;
    use std::time::Duration;
    use pollster::block_on;
    use super::*;
    #[test]
    fn waker_wait_until() {
        let mut sink = WakeSink::new();
        let source = sink.source();
        static FLAG: AtomicBool = AtomicBool::new(false);
        let t1 = thread::spawn(move || {
            std::thread::sleep(Duration::from_millis(10));
            source.notify();             std::thread::sleep(Duration::from_millis(10));
            FLAG.store(true, Ordering::Relaxed);
            source.notify();
        });
        let t2 = thread::spawn(move || {
            block_on(sink.wait_until(|| {
                if FLAG.load(Ordering::Relaxed) {
                    Some(())
                } else {
                    None
                }
            }));
            assert!(FLAG.load(Ordering::Relaxed));
        });
        t1.join().unwrap();
        t2.join().unwrap();
    }
    #[test]
    fn waker_ref_wait_until() {
        let mut w = DiatomicWaker::new();
        let mut sink = w.sink_ref();
        let source = sink.source_ref();
        static FLAG: AtomicBool = AtomicBool::new(false);
        thread::scope(|s| {
            s.spawn(move || {
                std::thread::sleep(Duration::from_millis(10));
                source.notify();                 std::thread::sleep(Duration::from_millis(10));
                FLAG.store(true, Ordering::Relaxed);
                source.notify();
            });
            s.spawn(move || {
                block_on(sink.wait_until(|| {
                    if FLAG.load(Ordering::Relaxed) {
                        Some(())
                    } else {
                        None
                    }
                }));
                assert!(FLAG.load(Ordering::Relaxed));
            });
        });
    }
}
#[cfg(all(test, diatomic_waker_loom))]
mod tests {
    use super::*;
    use core::task::Waker;
    use std::future::Future;
    use std::pin::Pin;
    use std::sync::atomic::Ordering;
    use std::sync::Arc;
    use std::task::{Context, Poll};
    use loom::model::Builder;
    use loom::sync::atomic::{AtomicU32, AtomicUsize};
    use loom::thread;
    use waker_fn::waker_fn;
        #[derive(Clone, Default)]
    struct MultiWaker {
        state: Arc<AtomicU32>,
    }
    impl MultiWaker {
                                                                fn take_notification(&self) -> bool {
                        let mut state = self.state.load(Ordering::Relaxed);
            loop {
                                                let notified_stated = state | 1;
                let unnotified_stated = state & !1;
                match self.state.compare_exchange_weak(
                    notified_stated,
                    unnotified_stated,
                    Ordering::Acquire,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => return true,
                    Err(s) => {
                        state = s;
                        if state == unnotified_stated {
                            return false;
                        }
                    }
                }
            }
        }
                fn new_waker(&self) -> Waker {
                        let mut state = self.state.load(Ordering::Relaxed);
            let mut epoch;
            loop {
                                epoch = (state & !1) + 2;
                match self.state.compare_exchange_weak(
                    state,
                    epoch,
                    Ordering::Relaxed,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => break,
                    Err(s) => state = s,
                }
            }
                        let waker_state = self.state.clone();
            waker_fn(move || {
                let mut state = waker_state.load(Ordering::Relaxed);
                loop {
                    let new_state = if state & !1 == epoch {
                        epoch | 1
                    } else {
                        break;
                    };
                    match waker_state.compare_exchange(
                        state,
                        new_state,
                        Ordering::Release,
                        Ordering::Relaxed,
                    ) {
                        Ok(_) => break,
                        Err(s) => state = s,
                    }
                }
            })
        }
    }
                    #[derive(Clone, Default)]
    struct Counter {
        count: Arc<AtomicUsize>,
    }
    impl Counter {
        fn increment(&self) {
            self.count.fetch_add(1, Ordering::Relaxed);
        }
        fn try_decrement(&self) -> bool {
            let mut count = self.count.load(Ordering::Relaxed);
            loop {
                if count == 0 {
                    return false;
                }
                match self.count.compare_exchange(
                    count,
                    count - 1,
                    Ordering::Relaxed,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => return true,
                    Err(c) => count = c,
                }
            }
        }
    }
                                            fn loom_notify(
        token_count: usize,
        max_spurious_wake: usize,
        change_waker: bool,
        preemption_bound: usize,
    ) {
                let mut builder = Builder::new();
        if builder.preemption_bound.is_none() {
            builder.preemption_bound = Some(preemption_bound);
        }
        builder.check(move || {
            let token_counter = Counter::default();
            let mut wake_sink = WakeSink::new();
            for src_id in 0..token_count {
                thread::spawn({
                    let token_counter = token_counter.clone();
                    let wake_src = wake_sink.source();
                    move || {
                        if src_id < max_spurious_wake {
                            wake_src.notify();
                        }
                        token_counter.increment();
                        wake_src.notify();
                    }
                });
            }
            let multi_waker = MultiWaker::default();
            let mut waker = multi_waker.new_waker();
            let mut satisfied_predicates_count = 0;
                                                                                    while satisfied_predicates_count < token_count {
                let mut wait_until = wake_sink.wait_until(|| {
                    if token_counter.try_decrement() {
                        Some(())
                    } else {
                        None
                    }
                });
                                loop {
                    let mut cx = Context::from_waker(&waker);
                    let poll_state = Pin::new(&mut wait_until).poll(&mut cx);
                    if poll_state == Poll::Ready(()) {
                        satisfied_predicates_count += 1;
                        break;
                    }
                                                            while !multi_waker.take_notification() {
                        thread::yield_now();
                    }
                    if change_waker {
                        waker = multi_waker.new_waker();
                    }
                }
            }
        });
    }
    #[test]
    fn loom_notify_two_tokens() {
        const DEFAULT_PREEMPTION_BOUND: usize = 4;
        loom_notify(2, 0, false, DEFAULT_PREEMPTION_BOUND);
    }
    #[test]
    fn loom_notify_two_tokens_one_spurious() {
        const DEFAULT_PREEMPTION_BOUND: usize = 4;
        loom_notify(2, 1, false, DEFAULT_PREEMPTION_BOUND);
    }
    #[test]
    fn loom_notify_two_tokens_change_waker() {
        const DEFAULT_PREEMPTION_BOUND: usize = 3;
        loom_notify(2, 0, true, DEFAULT_PREEMPTION_BOUND);
    }
    #[test]
    fn loom_notify_two_tokens_one_spurious_change_waker() {
        const DEFAULT_PREEMPTION_BOUND: usize = 3;
        loom_notify(2, 1, true, DEFAULT_PREEMPTION_BOUND);
    }
    #[test]
    fn loom_notify_three_tokens() {
        const DEFAULT_PREEMPTION_BOUND: usize = 2;
        loom_notify(3, 0, false, DEFAULT_PREEMPTION_BOUND);
    }
    #[test]
                fn loom_waker_slot_reuse() {
                        const DEFAULT_PREEMPTION_BOUND: usize = 5;
                        let mut builder = Builder::new();
        if builder.preemption_bound.is_none() {
            builder.preemption_bound = Some(DEFAULT_PREEMPTION_BOUND);
        }
        builder.check(move || {
            let mut wake_sink = WakeSink::new();
            thread::spawn({
                let wake_src = wake_sink.source();
                move || {
                    wake_src.notify();
                }
            });
            thread::spawn({
                let wake_src = wake_sink.source();
                move || {
                    wake_src.notify();
                    wake_src.notify();
                }
            });
            let multi_waker = MultiWaker::default();
            for _ in 0..3 {
                let waker = multi_waker.new_waker();
                wake_sink.register(&waker);
            }
        });
    }
}