Files
RedBear-OS/src/sync/wait_condition.rs
T
2024-03-16 17:49:31 +01:00

93 lines
2.3 KiB
Rust

use alloc::{sync::Arc, vec::Vec};
use spin::{Mutex, MutexGuard};
use spinning_top::RwSpinlock;
use crate::context::{self, Context};
#[derive(Debug)]
pub struct WaitCondition {
contexts: Mutex<Vec<Arc<RwSpinlock<Context>>>>,
}
impl WaitCondition {
pub const fn new() -> WaitCondition {
WaitCondition {
contexts: Mutex::new(Vec::new()),
}
}
// Notify all waiters
pub fn notify(&self) -> usize {
let mut contexts = self.contexts.lock();
let len = contexts.len();
while let Some(context_lock) = contexts.pop() {
context_lock.write().unblock();
}
len
}
// Notify as though a signal woke the waiters
pub unsafe fn notify_signal(&self) -> usize {
let contexts = self.contexts.lock();
let len = contexts.len();
for context_lock in contexts.iter() {
context_lock.write().unblock();
}
len
}
// Wait until notified. Unlocks guard when blocking is ready. Returns false if resumed by a signal or the notify_signal function
pub fn wait<T>(&self, guard: MutexGuard<T>, reason: &'static str) -> bool {
let id;
{
let context_lock = {
let contexts = context::contexts();
let context_lock = contexts.current().expect("WaitCondition::wait: no context");
Arc::clone(context_lock)
};
{
let mut context = context_lock.write();
id = context.id;
context.block(reason);
}
self.contexts.lock().push(context_lock);
drop(guard);
}
context::switch();
let mut waited = true;
{
let mut contexts = self.contexts.lock();
let mut i = 0;
while i < contexts.len() {
let remove = {
let context = contexts[i].read();
context.id == id
};
if remove {
contexts.remove(i);
waited = false;
break;
} else {
i += 1;
}
}
}
waited
}
}
impl Drop for WaitCondition {
fn drop(&mut self) {
unsafe { self.notify_signal() };
}
}