feat: add process wait channels
This commit is contained in:
@@ -35,6 +35,7 @@ impl IntSource for GenericTimer {
|
||||
CNTP_TVAL_EL0.set(TIMER_TICK);
|
||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
|
||||
use crate::proc;
|
||||
proc::wait::tick();
|
||||
proc::switch();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Timer interface
|
||||
|
||||
use crate::dev::Device;
|
||||
use crate::proc::Pid;
|
||||
use core::time::Duration;
|
||||
use error::Errno;
|
||||
|
||||
@@ -9,3 +10,9 @@ pub trait TimestampSource: Device {
|
||||
/// Reads current timestamp as a [Duration] from system start time
|
||||
fn timestamp(&self) -> Result<Duration, Errno>;
|
||||
}
|
||||
|
||||
///
|
||||
pub struct Sleep {
|
||||
deadline: Duration,
|
||||
pid: Pid
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
const_panic,
|
||||
panic_info_message,
|
||||
alloc_error_handler,
|
||||
linked_list_cursors,
|
||||
const_btree_new
|
||||
)]
|
||||
#![no_std]
|
||||
|
||||
@@ -8,6 +8,9 @@ pub mod elf;
|
||||
pub mod process;
|
||||
pub use process::{Pid, Process, ProcessRef, State as ProcessState};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub mod wait;
|
||||
|
||||
pub mod sched;
|
||||
pub use sched::Scheduler;
|
||||
pub(self) use sched::SCHED;
|
||||
@@ -38,6 +41,11 @@ pub fn switch() {
|
||||
SCHED.switch(false);
|
||||
}
|
||||
|
||||
///
|
||||
pub fn process(id: Pid) -> ProcessRef {
|
||||
PROCESSES.lock().get(&id).unwrap().clone()
|
||||
}
|
||||
|
||||
/// Global list of all processes in the system
|
||||
pub(self) static PROCESSES: IrqSafeSpinLock<BTreeMap<Pid, ProcessRef>> =
|
||||
IrqSafeSpinLock::new(BTreeMap::new());
|
||||
|
||||
@@ -44,6 +44,7 @@ struct ProcessInner {
|
||||
space: Option<&'static mut Space>,
|
||||
state: State,
|
||||
id: Pid,
|
||||
wait_flag: bool,
|
||||
exit: Option<ExitCode>,
|
||||
}
|
||||
|
||||
@@ -161,7 +162,7 @@ impl Process {
|
||||
assert_eq!(src_lock.state, State::Running);
|
||||
src_lock.state = State::Ready;
|
||||
}
|
||||
assert_eq!(dst_lock.state, State::Ready);
|
||||
assert!(dst_lock.state == State::Ready || dst_lock.state == State::Waiting);
|
||||
dst_lock.state = State::Running;
|
||||
}
|
||||
|
||||
@@ -171,6 +172,30 @@ impl Process {
|
||||
(&mut *src_ctx).switch(&mut *dst_ctx);
|
||||
}
|
||||
|
||||
///
|
||||
pub fn enter_wait(&self) {
|
||||
let drop = {
|
||||
let mut lock = self.inner.lock();
|
||||
let drop = lock.state == State::Running;
|
||||
lock.state = State::Waiting;
|
||||
SCHED.dequeue(lock.id);
|
||||
drop
|
||||
};
|
||||
if drop {
|
||||
SCHED.switch(true);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn set_wait_flag(&self, v: bool) {
|
||||
self.inner.lock().wait_flag = v;
|
||||
}
|
||||
|
||||
///
|
||||
pub fn wait_flag(&self) -> bool {
|
||||
self.inner.lock().wait_flag
|
||||
}
|
||||
|
||||
/// Returns the process ID
|
||||
pub fn id(&self) -> Pid {
|
||||
self.inner.lock().id
|
||||
@@ -185,6 +210,7 @@ impl Process {
|
||||
id,
|
||||
exit: None,
|
||||
space: None,
|
||||
wait_flag: false,
|
||||
state: State::Ready,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
use crate::arch::machine;
|
||||
use crate::proc::{self, sched::SCHED, Pid, Process, ProcessState};
|
||||
use crate::dev::timer::TimestampSource;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use alloc::{vec::Vec, collections::{VecDeque, LinkedList}};
|
||||
use core::time::Duration;
|
||||
use error::Errno;
|
||||
|
||||
pub struct Wait {
|
||||
queue: IrqSafeSpinLock<LinkedList<Pid>>,
|
||||
}
|
||||
|
||||
pub struct Timeout {
|
||||
pid: Pid,
|
||||
deadline: Duration
|
||||
}
|
||||
|
||||
static TICK_LIST: IrqSafeSpinLock<LinkedList<Timeout>> = IrqSafeSpinLock::new(LinkedList::new());
|
||||
|
||||
pub fn tick() {
|
||||
let time = machine::local_timer().timestamp().unwrap();
|
||||
let mut list = TICK_LIST.lock();
|
||||
let mut cursor = list.cursor_front_mut();
|
||||
|
||||
while let Some(item) = cursor.current() {
|
||||
if time > item.deadline {
|
||||
let pid = item.pid;
|
||||
cursor.remove_current();
|
||||
SCHED.enqueue(pid);
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sleep(timeout: Duration) {
|
||||
// Dummy wait descriptor which will never receive notifications
|
||||
static SLEEP_NOTIFY: Wait = Wait::new();
|
||||
SLEEP_NOTIFY.sleep_on(Some(timeout));
|
||||
}
|
||||
|
||||
impl Wait {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
queue: IrqSafeSpinLock::new(LinkedList::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wakeup_all(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn wakeup_one(&self) {
|
||||
// No IRQs will arrive now == safe to manipulate tick list
|
||||
let mut tick_lock = TICK_LIST.lock();
|
||||
let pid = self.queue.lock().pop_front();
|
||||
if let Some(pid) = pid {
|
||||
let mut cursor = tick_lock.cursor_front_mut();
|
||||
while let Some(item) = cursor.current() {
|
||||
if pid == item.pid {
|
||||
cursor.remove_current();
|
||||
break;
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
}
|
||||
drop(tick_lock);
|
||||
|
||||
proc::process(pid).set_wait_flag(false);
|
||||
SCHED.enqueue(pid);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sleep_on(&self, timeout: Option<Duration>) -> Result<(), Errno> {
|
||||
let proc = Process::current();
|
||||
let deadline = timeout.map(|t| machine::local_timer().timestamp().unwrap() + t);
|
||||
let mut queue_lock = Some(self.queue.lock());
|
||||
|
||||
queue_lock.as_mut().unwrap().push_back(proc.id());
|
||||
proc.set_wait_flag(true);
|
||||
if let Some(deadline) = deadline {
|
||||
TICK_LIST.lock().push_back(Timeout {
|
||||
pid: proc.id(),
|
||||
deadline
|
||||
});
|
||||
}
|
||||
|
||||
loop {
|
||||
if !proc.wait_flag() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
queue_lock = None;
|
||||
proc.enter_wait();
|
||||
queue_lock = Some(self.queue.lock());
|
||||
|
||||
if let Some(deadline) = deadline {
|
||||
if machine::local_timer().timestamp()? > deadline {
|
||||
let mut cursor = queue_lock.as_mut().unwrap().cursor_front_mut();
|
||||
|
||||
while let Some(&mut item) = cursor.current() {
|
||||
if proc.id() == item {
|
||||
cursor.remove_current();
|
||||
break;
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
}
|
||||
|
||||
return Err(Errno::TimedOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,15 @@ pub unsafe fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
||||
println!(Level::Debug, "");
|
||||
Ok(args[1])
|
||||
}
|
||||
// sys_ex_sleep
|
||||
121 => {
|
||||
use crate::proc::wait;
|
||||
use core::time::Duration;
|
||||
|
||||
wait::sleep(Duration::from_nanos(args[0] as u64));
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
_ => panic!("Undefined system call: {}", num),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user