1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use core::time::Duration;
#[cfg(feature = "std")]
use std::sync::{Condvar, Mutex};

pub trait Instant {
    fn now() -> Self;
    fn elapsed(&self) -> Duration;
}

pub trait Semaphore: Default {
    type Instant: Instant;

    fn wait<F: Fn() -> bool>(&self, f: F, timeout: Option<Duration>) -> bool;
    fn notify<F: FnOnce()>(&self, f: F);
}

#[cfg(feature = "std")]
pub use std::time::Instant as StdInstant;

#[cfg(feature = "std")]
impl Instant for StdInstant {
    fn now() -> Self {
        StdInstant::now()
    }
    fn elapsed(&self) -> Duration {
        StdInstant::elapsed(self)
    }
}

#[cfg(feature = "std")]
#[derive(Default)]
pub struct StdSemaphore {
    condvar: Condvar,
    mutex: Mutex<()>,
}

#[cfg(feature = "std")]
impl Semaphore for StdSemaphore {
    type Instant = StdInstant;

    fn wait<F: Fn() -> bool>(&self, f: F, timeout: Option<Duration>) -> bool {
        if f() {
            return true;
        }
        let mut guard_slot = Some(self.mutex.lock().unwrap());
        for timeout in TimeoutIterator::<Self::Instant>::new(timeout) {
            if f() {
                return true;
            }
            let guard = guard_slot.take().unwrap();
            guard_slot.replace(match timeout {
                Some(t) => self.condvar.wait_timeout(guard, t).unwrap().0,
                None => self.condvar.wait(guard).unwrap(),
            });
        }
        f()
    }
    fn notify<F: FnOnce()>(&self, f: F) {
        let _guard = self.mutex.lock();
        f();
        self.condvar.notify_all();
    }
}

#[derive(Clone, Debug)]
pub(crate) struct TimeoutIterator<I: Instant> {
    start: I,
    timeout: Option<Duration>,
}

impl<I: Instant> TimeoutIterator<I> {
    pub fn new(timeout: Option<Duration>) -> Self {
        Self { start: I::now(), timeout }
    }
}

impl<I: Instant> Iterator for TimeoutIterator<I> {
    type Item = Option<Duration>;
    fn next(&mut self) -> Option<Self::Item> {
        match self.timeout {
            Some(dur) => {
                let elapsed = self.start.elapsed();
                if dur > elapsed {
                    Some(Some(dur - elapsed))
                } else {
                    None
                }
            }
            None => Some(None),
        }
    }
}