From 0680023e359285d09d3a73669076441207ee5ecd Mon Sep 17 00:00:00 2001 From: WangRunji Date: Fri, 26 Oct 2018 00:49:19 +0800 Subject: [PATCH] Recover wait/sleep --- crate/process/src/process_manager.rs | 64 ++++++++++++++++++++++++++-- crate/process/src/thread.rs | 25 +++++++---- kernel/src/fs.rs | 5 ++- kernel/src/syscall.rs | 25 ++++++++--- kernel/src/trap.rs | 7 ++- 5 files changed, 107 insertions(+), 19 deletions(-) diff --git a/crate/process/src/process_manager.rs b/crate/process/src/process_manager.rs index b7e5d47..f4ed274 100644 --- a/crate/process/src/process_manager.rs +++ b/crate/process/src/process_manager.rs @@ -3,6 +3,8 @@ use alloc::sync::Arc; use spin::Mutex; use scheduler::Scheduler; use core::cell::UnsafeCell; +use alloc::vec::Vec; +use event_hub::EventHub; struct Process { id: Pid, @@ -20,10 +22,15 @@ pub enum Status { Ready, Running(usize), Sleeping, + Waiting(Pid), /// aka ZOMBIE. Its context was dropped. Exited(ExitCode), } +enum Event { + Wakeup(Pid), +} + pub trait Context { unsafe fn switch_to(&mut self, target: &mut Context); } @@ -31,6 +38,8 @@ pub trait Context { pub struct ProcessManager { procs: [Mutex>; MAX_PROC_NUM], scheduler: Mutex>, + wait_queue: [Mutex>; MAX_PROC_NUM], + event_hub: Mutex>, } impl ProcessManager { @@ -38,6 +47,8 @@ impl ProcessManager { ProcessManager { procs: Default::default(), scheduler: Mutex::new(scheduler), + wait_queue: Default::default(), + event_hub: Mutex::new(EventHub::new()), } } @@ -53,7 +64,6 @@ impl ProcessManager { /// Add a new process pub fn add(&self, context: Box) -> Pid { let pid = self.alloc_pid(); - // TODO: check parent *self.procs[pid].lock() = Some(Process { id: pid, status: Status::Ready, @@ -68,6 +78,13 @@ impl ProcessManager { /// Return true if time slice == 0. /// Called by timer interrupt handler. pub fn tick(&self, pid: Pid) -> bool { + let mut event_hub = self.event_hub.lock(); + event_hub.tick(); + while let Some(event) = event_hub.pop() { + match event { + Event::Wakeup(pid) => self.set_status(pid, Status::Ready), + } + } self.scheduler.lock().tick(pid) } @@ -107,18 +124,22 @@ impl ProcessManager { /// Switch the status of a process. /// Insert/Remove it to/from scheduler if necessary. - pub fn set_status(&self, pid: Pid, status: Status) { + fn set_status(&self, pid: Pid, status: Status) { let mut scheduler = self.scheduler.lock(); let mut proc_lock = self.procs[pid].lock(); let mut proc = proc_lock.as_mut().unwrap(); + trace!("process {} {:?} -> {:?}", pid, proc.status, status); match (&proc.status, &status) { (Status::Ready, Status::Ready) => return, (Status::Ready, _) => scheduler.remove(pid), (Status::Running(_), _) => {}, + (Status::Exited(_), _) => panic!("can not set status for a exited process"), + (Status::Waiting(target), Status::Exited(_)) => + self.wait_queue[*target].lock().retain(|&i| i != pid), + // TODO: Sleep -> Exited Remove wakeup event. (_, Status::Ready) => scheduler.insert(pid), _ => {} } - trace!("process {} {:?} -> {:?}", pid, proc.status, status); match proc.status { Status::Running(_) => proc.status_after_stop = status, _ => proc.status = status, @@ -128,4 +149,41 @@ impl ProcessManager { _ => {} } } + + + pub fn get_status(&self, pid: Pid) -> Option { + self.procs[pid].lock().as_ref().map(|p| p.status.clone()) + } + + pub fn remove(&self, pid: Pid) { + let mut proc_lock = self.procs[pid].lock(); + let proc = proc_lock.as_ref().unwrap(); + match proc.status { + Status::Exited(_) => *proc_lock = None, + _ => panic!("can not remove non-exited process"), + } + } + + pub fn sleep(&self, pid: Pid, time: usize) { + self.set_status(pid, Status::Sleeping); + if time != 0 { + self.event_hub.lock().push(time, Event::Wakeup(pid)); + } + } + + pub fn wakeup(&self, pid: Pid) { + self.set_status(pid, Status::Ready); + } + + pub fn wait(&self, pid: Pid, target: Pid) { + self.set_status(pid, Status::Waiting(target)); + self.wait_queue[target].lock().push(pid); + } + + pub fn exit(&self, pid: Pid, code: ExitCode) { + self.set_status(pid, Status::Exited(code)); + for waiter in self.wait_queue[pid].lock().drain(..) { + self.wakeup(waiter); + } + } } diff --git a/crate/process/src/thread.rs b/crate/process/src/thread.rs index c9e8a6f..1c8b1ad 100644 --- a/crate/process/src/thread.rs +++ b/crate/process/src/thread.rs @@ -41,7 +41,7 @@ pub fn current() -> Thread { pub fn sleep(dur: Duration) { let time = dur_to_ticks(dur); info!("sleep: {:?} ticks", time); - // TODO: register wakeup + processor().manager().sleep(current().id(), time); park(); fn dur_to_ticks(dur: Duration) -> usize { @@ -87,9 +87,8 @@ pub fn spawn(f: F) -> JoinHandle // unsafe { LocalKey::::get_map() }.clear(); // 让Processor退出当前线程 // 把f返回值在堆上的指针,以线程返回码的形式传递出去 - let pid = processor().pid(); let exit_code = Box::into_raw(ret) as usize; - processor().manager().set_status(pid, Status::Exited(exit_code)); + processor().manager().exit(current().id(), exit_code); processor().yield_now(); // 再也不会被调度回来了 unreachable!() @@ -116,8 +115,7 @@ pub fn yield_now() { /// Blocks unless or until the current thread's token is made available. pub fn park() { info!("park:"); - let pid = processor().pid(); - processor().manager().set_status(pid, Status::Sleeping); + processor().manager().sleep(current().id(), 0); processor().yield_now(); } @@ -129,7 +127,7 @@ pub struct Thread { impl Thread { /// Atomically makes the handle's token available if it is not already. pub fn unpark(&self) { - processor().manager().set_status(self.pid, Status::Ready); + processor().manager().wakeup(self.pid); } /// Gets the thread's unique identifier. pub fn id(&self) -> usize { @@ -150,8 +148,19 @@ impl JoinHandle { } /// Waits for the associated thread to finish. pub fn join(self) -> Result { - // TODO: wait for the thread - unimplemented!() + loop { + match processor().manager().get_status(self.thread.pid) { + Some(Status::Exited(exit_code)) => { + processor().manager().remove(self.thread.pid); + // Find return value on the heap from the exit code. + return Ok(unsafe { *Box::from_raw(exit_code as *mut T) }); + } + None => return Err(()), + _ => {} + } + processor().manager().wait(current().id(), self.thread.pid); + processor().yield_now(); + } } } diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 41954c0..27a2b74 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -46,8 +46,9 @@ pub fn shell() { if let Ok(file) = root.borrow().lookup(name.as_str()) { use process::*; let len = file.borrow().read_at(0, &mut *buf).unwrap(); - processor().manager().add(ContextImpl::new_user(&buf[..len])); - // TODO: wait for new process + let pid = processor().manager().add(ContextImpl::new_user(&buf[..len])); + processor().manager().wait(thread::current().id(), pid); + processor().yield_now(); } else { println!("Program not exist"); } diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index 6dbd056..f121688 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -68,7 +68,22 @@ fn sys_fork(tf: &TrapFrame) -> i32 { /// Wait the process exit. /// Return the PID. Store exit code to `code` if it's not null. fn sys_wait(pid: usize, code: *mut i32) -> i32 { - unimplemented!(); + assert_ne!(pid, 0, "wait for 0 is not supported yet"); + loop { + match processor().manager().get_status(pid) { + Some(Status::Exited(exit_code)) => { + if !code.is_null() { + unsafe { code.write(exit_code as i32); } + } + processor().manager().remove(pid); + return 0; + } + None => return -1, + _ => {} + } + processor().manager().wait(thread::current().id(), pid); + processor().yield_now(); + } } fn sys_yield() -> i32 { @@ -78,7 +93,7 @@ fn sys_yield() -> i32 { /// Kill the process fn sys_kill(pid: usize) -> i32 { - processor().manager().set_status(pid, Status::Exited(0x100)); + processor().manager().exit(pid, 0x100); 0 } @@ -90,9 +105,9 @@ fn sys_getpid() -> i32 { /// Exit the current process fn sys_exit(exit_code: usize) -> i32 { let pid = thread::current().id(); - processor().manager().set_status(pid, Status::Exited(exit_code)); + processor().manager().exit(pid, exit_code); processor().yield_now(); - 0 + unreachable!(); } fn sys_sleep(time: usize) -> i32 { @@ -102,7 +117,7 @@ fn sys_sleep(time: usize) -> i32 { } fn sys_get_time() -> i32 { - unimplemented!(); + unsafe { ::trap::TICK as i32 } } fn sys_lab6_set_priority(priority: usize) -> i32 { diff --git a/kernel/src/trap.rs b/kernel/src/trap.rs index 6cd4556..7ed9e21 100644 --- a/kernel/src/trap.rs +++ b/kernel/src/trap.rs @@ -2,8 +2,13 @@ use process::*; use arch::interrupt::TrapFrame; use arch::cpu; +pub static mut TICK: usize = 0; + pub fn timer() { processor().tick(); + if cpu::id() == 0 { + unsafe { TICK += 1; } + } } pub fn before_return() { @@ -13,7 +18,7 @@ pub fn error(tf: &TrapFrame) -> ! { let pid = processor().pid(); error!("On CPU{} Process {}:\n{:#x?}", cpu::id(), pid, tf); - processor().manager().set_status(pid, Status::Exited(0x100)); + processor().manager().exit(pid, 0x100); processor().yield_now(); unreachable!(); } \ No newline at end of file