From 2ad61cae6500c4c3b878b04a9bb5034d5f73635a Mon Sep 17 00:00:00 2001 From: WangRunji Date: Thu, 31 May 2018 20:26:25 +0800 Subject: [PATCH] Impl std-like thread interface. --- src/arch/x86_64/interrupt/trapframe.rs | 7 +- src/lib.rs | 1 + src/process/mod.rs | 11 +- src/process/process.rs | 4 +- src/process/processor.rs | 15 ++- src/syscall.rs | 12 +-- src/thread.rs | 135 +++++++++++++++++++++++++ 7 files changed, 167 insertions(+), 18 deletions(-) create mode 100644 src/thread.rs diff --git a/src/arch/x86_64/interrupt/trapframe.rs b/src/arch/x86_64/interrupt/trapframe.rs index b8d0dba..cad4c77 100644 --- a/src/arch/x86_64/interrupt/trapframe.rs +++ b/src/arch/x86_64/interrupt/trapframe.rs @@ -35,9 +35,10 @@ pub struct TrapFrame { /// 用于在内核栈中构造新线程的中断帧 impl TrapFrame { - fn new_kernel_thread(entry: extern fn() -> !, rsp: usize) -> Self { + fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, rsp: usize) -> Self { use arch::gdt; let mut tf = TrapFrame::default(); + tf.rdi = arg; tf.cs = gdt::KCODE_SELECTOR.0 as usize; tf.rip = entry as usize; tf.ss = gdt::KDATA_SELECTOR.0 as usize; @@ -87,11 +88,11 @@ pub struct InitStack { } impl InitStack { - pub fn new_kernel_thread(entry: extern fn() -> !, rsp: usize) -> Self { + pub fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, rsp: usize) -> Self { InitStack { context: Context::new(), trapret: trap_ret as usize, - tf: TrapFrame::new_kernel_thread(entry, rsp), + tf: TrapFrame::new_kernel_thread(entry, arg, rsp), } } pub fn new_user_thread(entry_addr: usize, rsp: usize, is32: bool) -> Self { diff --git a/src/lib.rs b/src/lib.rs index 411a090..c477e2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,7 @@ mod consts; mod process; mod syscall; mod fs; +mod thread; mod sync; #[allow(dead_code)] diff --git a/src/process/mod.rs b/src/process/mod.rs index 078ec0d..b1e2bb7 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -15,7 +15,7 @@ mod scheduler; pub fn init(mut mc: MemoryController) { PROCESSOR.call_once(|| {Mutex::new({ let initproc = Process::new_init(&mut mc); - let idleproc = Process::new("idle", idle_thread, &mut mc); + let idleproc = Process::new("idle", idle_thread, 0, &mut mc); let mut processor = Processor::new(); processor.add(initproc); processor.add(idleproc); @@ -27,7 +27,7 @@ pub fn init(mut mc: MemoryController) { pub static PROCESSOR: Once> = Once::new(); pub static MC: Once> = Once::new(); -extern fn idle_thread() -> ! { +extern fn idle_thread(arg: usize) -> ! { loop { println!("idle ..."); let mut i = 0; @@ -45,6 +45,13 @@ pub fn add_user_process(name: impl AsRef, data: &[u8]) { processor.add(new); } +pub fn add_kernel_process(entry: extern fn(usize) -> !, arg: usize) -> Pid { + let mut processor = PROCESSOR.try().unwrap().lock(); + let mut mc = MC.try().unwrap().lock(); + let mut new = Process::new("", entry, arg, &mut mc); + processor.add(new) +} + pub fn print() { debug!("{:#x?}", *PROCESSOR.try().unwrap().lock()); } \ No newline at end of file diff --git a/src/process/process.rs b/src/process/process.rs index 0e31d33..0f2fee2 100644 --- a/src/process/process.rs +++ b/src/process/process.rs @@ -32,9 +32,9 @@ pub enum Status { impl Process { /// Make a new kernel thread - pub fn new(name: &str, entry: extern fn() -> !, mc: &mut MemoryController) -> Self { + pub fn new(name: &str, entry: extern fn(usize) -> !, arg: usize, mc: &mut MemoryController) -> Self { let kstack = mc.alloc_stack(7).unwrap(); - let data = InitStack::new_kernel_thread(entry, kstack.top()); + let data = InitStack::new_kernel_thread(entry, arg, kstack.top()); let rsp = kstack.push_at_top(data); Process { diff --git a/src/process/processor.rs b/src/process/processor.rs index 5c30abc..4d67a10 100644 --- a/src/process/processor.rs +++ b/src/process/processor.rs @@ -17,9 +17,10 @@ pub struct Processor { /// Choose what on next schedule ? next: Option, // WARNING: if MAX_PROCESS_NUM is too large, will cause stack overflow - scheduler: StrideScheduler, + scheduler: RRScheduler, } +// TODO: 除schedule()外的其它函数,应该只设置进程状态,不应调用schedule impl Processor { pub fn new() -> Self { Processor { @@ -29,12 +30,13 @@ impl Processor { kernel_page_table: None, next: None, // NOTE: max_time_slice <= 5 to ensure 'priority' test pass - scheduler: StrideScheduler::new(5), + scheduler: RRScheduler::new(100), } } pub fn lab6_set_priority(&mut self, priority: u8) { - self.scheduler.set_priority(self.current_pid, priority); + unimplemented!(); +// self.scheduler.set_priority(self.current_pid, priority); } pub fn set_reschedule(&mut self) { @@ -153,6 +155,7 @@ impl Processor { info!("Processor: switch from {} to {}\n rsp: ??? -> {:#x}", pid0, pid, to.rsp); unsafe { + // FIXME: safely pass MutexGuard use core::mem::forget; super::PROCESSOR.try().unwrap().force_unlock(); switch(&mut from.rsp, to.rsp); @@ -191,6 +194,12 @@ impl Processor { self.set_status(pid, Status::Sleeping); self.event_hub.push(time, Event::Wakeup(pid)); } + pub fn sleep_(&mut self, pid: Pid) { + self.set_status(pid, Status::Sleeping); + } + pub fn wakeup_(&mut self, pid: Pid) { + self.set_status(pid, Status::Ready); + } /// Let current process wait for another pub fn current_wait_for(&mut self, pid: Pid) -> WaitResult { diff --git a/src/syscall.rs b/src/syscall.rs index f8ec6ee..069f662 100644 --- a/src/syscall.rs +++ b/src/syscall.rs @@ -2,6 +2,7 @@ use arch::interrupt::TrapFrame; use process::*; +use thread; use util; /// 系统调用入口点 @@ -108,9 +109,7 @@ fn sys_wait(pid: usize, code: *mut i32) -> i32 { } fn sys_yield() -> i32 { - info!("yield:"); - let mut processor = PROCESSOR.try().unwrap().lock(); - processor.set_reschedule(); + thread::yield_now(); 0 } @@ -122,7 +121,7 @@ fn sys_kill(pid: usize) -> i32 { /// Get the current process id fn sys_getpid() -> i32 { - PROCESSOR.try().unwrap().lock().current_pid() as i32 + thread::current().id() as i32 } /// Exit the current process @@ -134,10 +133,7 @@ fn sys_exit(error_code: usize) -> i32 { } fn sys_sleep(time: usize) -> i32 { - info!("sleep: {} ticks", time); - let mut processor = PROCESSOR.try().unwrap().lock(); - let pid = processor.current_pid(); - processor.sleep(pid, time); + thread::sleep(time); 0 } diff --git a/src/thread.rs b/src/thread.rs new file mode 100644 index 0000000..789bb9c --- /dev/null +++ b/src/thread.rs @@ -0,0 +1,135 @@ +//! Thread std-like interface +//! +//! Based on process mod. +//! Used in the kernel. + +use process::*; +use core::marker::PhantomData; +use core::ptr; +use alloc::boxed::Box; + +/// Gets a handle to the thread that invokes it. +pub fn current() -> Thread { + Thread { + pid: PROCESSOR.try().unwrap().lock().current_pid(), + } +} + +/// Puts the current thread to sleep for the specified amount of time. +pub fn sleep(time: usize) { + // TODO: use core::time::Duration + info!("sleep: {} ticks", time); + let mut processor = PROCESSOR.try().unwrap().lock(); + let pid = processor.current_pid(); + processor.sleep(pid, time); + processor.schedule(); +} + +/// Spawns a new thread, returning a JoinHandle for it. +pub fn spawn(f: F) -> JoinHandle + where + F: Send + 'static + FnOnce() -> T, + T: Send + 'static, +{ + use process; + let pid = process::add_kernel_process(kernel_thread_entry::, &f as *const _ as usize); + return JoinHandle { + thread: Thread { pid }, + mark: PhantomData, + }; + + extern fn kernel_thread_entry(f: usize) -> ! + where + F: Send + 'static + FnOnce() -> T, + T: Send + 'static, + { + debug!("kernel_thread_entry"); + let f = unsafe { ptr::read(f as *mut F) }; + let ret = Box::new(f()); + let mut processor = PROCESSOR.try().unwrap().lock(); + let pid = processor.current_pid(); + processor.exit(pid, Box::into_raw(ret) as usize); + unreachable!() + } +} + +/// Cooperatively gives up a timeslice to the OS scheduler. +pub fn yield_now() { + info!("yield:"); + let mut processor = PROCESSOR.try().unwrap().lock(); + processor.set_reschedule(); + processor.schedule(); +} + +/// Blocks unless or until the current thread's token is made available. +pub fn park() { + let mut processor = PROCESSOR.try().unwrap().lock(); + let pid = processor.current_pid(); + processor.sleep_(pid); + processor.schedule(); +} + +/// A handle to a thread. +pub struct Thread { + pid: usize, +} + +impl Thread { + /// Atomically makes the handle's token available if it is not already. + pub fn unpark(&self) { + let mut processor = PROCESSOR.try().unwrap().lock(); + processor.wakeup_(self.pid); + } + /// Gets the thread's unique identifier. + pub fn id(&self) -> usize { + self.pid + } +} + +/// An owned permission to join on a thread (block on its termination). +pub struct JoinHandle { + thread: Thread, + mark: PhantomData, +} + +impl JoinHandle { + /// Extracts a handle to the underlying thread. + pub fn thread(&self) -> &Thread { + &self.thread + } + /// Waits for the associated thread to finish. + pub fn join(self) -> Result { + let mut processor = PROCESSOR.try().unwrap().lock(); + match processor.current_wait_for(self.thread.pid) { + WaitResult::Ok(_, exit_code) => { + unsafe { + let value = Box::from_raw(exit_code as *mut T); + Ok(ptr::read(exit_code as *const T)) + } + } + WaitResult::NotExist => Err(()), + } + } +} + +pub mod test { + use thread; + + pub fn unpack() { + let parked_thread = thread::spawn(|| { + println!("Parking thread"); + thread::park(); + println!("Thread unparked"); + 5 + }); + + // Let some time pass for the thread to be spawned. + thread::sleep(200); + + println!("Unpark the thread"); + parked_thread.thread().unpark(); + + let ret = parked_thread.join().unwrap(); + assert_eq!(ret, 5); + } +}