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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use crate::MutexGuard;
use preemption::hold_preemption_no_timer_disable;
use mpmc_queue::Queue;
use sync::DeadlockPrevention;
use sync_spin::Spin;
use task::{get_my_current_task, TaskRef};

/// A condition variable.
///
/// Condition variables represent the ability to block a thread such that it
/// consumes no CPU time while waiting for an event to occur.
// TODO: Is there even a point to exposing this generic?
pub struct Condvar<P = Spin>
where
    P: DeadlockPrevention,
{
    inner: Queue<TaskRef, P>,
}

impl<P> Condvar<P>
where
    P: DeadlockPrevention,
{
    /// Returns a new condition variable.
    pub const fn new() -> Self {
        Self {
            inner: Queue::new(),
        }
    }

    /// Blocks the current thread until this condition variable receives a
    /// notification.
    ///
    /// Note that this function is susceptible to spurious wakeups. Condition
    /// variables normally have a boolean predicate associated with them, and
    /// the predicate must always be checked each time this function returns to
    /// protect against spurious wakeups.
    pub fn wait<'a, T: ?Sized>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
        let task = get_my_current_task().unwrap();
        let mutex = guard.mutex();

        let preemption_guard = self
            .inner
            .push_if_fail(task.clone(), || {
                drop(guard);
                let preemption_guard = hold_preemption_no_timer_disable();
                task.block().unwrap();
                Result::<(), _>::Err(preemption_guard)
            })
            .unwrap_err();
        drop(preemption_guard);

        loop {
            scheduler::schedule();

            match self.inner.push_if_fail(task.clone(), || {
                if let Some(mutex_guard) = mutex.try_lock() {
                    Ok(mutex_guard)
                } else {
                    let preemption_guard = hold_preemption_no_timer_disable();
                    task.block().unwrap();
                    Err(preemption_guard)
                }
            }) {
                Ok(mutex_guard) => return mutex_guard,
                Err(preemption_guard) => {
                    drop(preemption_guard);
                }
            }
        }
    }

    /// Blocks the current thread until this condition variable receives a
    /// notification and the provided condition is false.
    pub fn wait_while<'a, T, F>(
        &self,
        mut guard: MutexGuard<'a, T>,
        mut condition: F,
    ) -> MutexGuard<'a, T>
    where
        F: FnMut(&mut T) -> bool,
    {
        while condition(&mut *guard) {
            guard = self.wait(guard);
        }
        guard
    }

    fn notify_one_inner(&self) -> bool {
        loop {
            let task = match self.inner.pop() {
                Some(task) => task,
                None => return false,
            };

            if task.unblock().is_ok() {
                return true;
            }
        }
    }

    /// Wakes up one thread blocked on this condvar.
    pub fn notify_one(&self) {
        self.notify_one_inner();
    }

    /// Wakes up all threads blocked on this condvar.
    pub fn notify_all(&self) {
        while self.notify_one_inner() {}
    }
}