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() {}
}
}