From a2111a928fef2579954a4d4d7a876205d9d7356b Mon Sep 17 00:00:00 2001 From: WangRunji Date: Tue, 17 Jul 2018 19:06:30 +0800 Subject: [PATCH] Move thread mod to ucore-process crate --- crate/process/src/lib.rs | 1 + crate/process/src/processor.rs | 1 + crate/process/src/thread.rs | 243 +++++++++++++++++++++++++++++++++ kernel/src/lib.rs | 3 +- kernel/src/process/context.rs | 17 ++- kernel/src/process/mod.rs | 22 ++- kernel/src/sync/condvar.rs | 3 +- kernel/src/thread.rs | 200 --------------------------- 8 files changed, 278 insertions(+), 212 deletions(-) create mode 100644 crate/process/src/thread.rs delete mode 100644 kernel/src/thread.rs diff --git a/crate/process/src/lib.rs b/crate/process/src/lib.rs index a9d1cea..01e7b4e 100644 --- a/crate/process/src/lib.rs +++ b/crate/process/src/lib.rs @@ -15,5 +15,6 @@ extern crate std; pub mod processor; pub mod scheduler; +pub mod thread; mod util; mod event_hub; diff --git a/crate/process/src/processor.rs b/crate/process/src/processor.rs index e0b04fc..ffb1332 100644 --- a/crate/process/src/processor.rs +++ b/crate/process/src/processor.rs @@ -26,6 +26,7 @@ pub enum Status { pub trait Context: Debug { unsafe fn switch(&mut self, target: &mut Self); + fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Self; } pub struct Processor_ { diff --git a/crate/process/src/thread.rs b/crate/process/src/thread.rs new file mode 100644 index 0000000..a772d04 --- /dev/null +++ b/crate/process/src/thread.rs @@ -0,0 +1,243 @@ +//! Thread std-like interface +//! +//! Based on Processor. +//! Used in the kernel. +//! +//! # Example +//! +//! ``` +//! // Define a support implementation struct +//! pub struct ThreadSupportImpl; +//! +//! // Impl `ThreadSupport` trait +//! impl ThreadSupport for ThreadSupportImpl { ... } +//! +//! // Export the full struct as `thread`. +//! #[allow(non_camel_case_types)] +//! pub type thread = ThreadMod; +//! ``` +//! +//! ``` +//! // Use it just like `std::thread` +//! use thread; +//! let t = thread::current(); +//! +//! // But the other struct is not available ... +//! let t: thread::Thread; // ERROR! +//! ``` + +use alloc::boxed::Box; +use alloc::BTreeMap; +use core::any::Any; +use core::marker::PhantomData; +use core::ptr; +use core::time::Duration; +use core::ops::DerefMut; +use processor::*; +use scheduler::Scheduler; + +/// All dependencies for thread mod. +pub trait ThreadSupport { + type Context: Context; + type Scheduler: Scheduler; + type ProcessorGuard: DerefMut>; + fn processor() -> Self::ProcessorGuard; +} + +/// Root structure served as thread mod +pub struct ThreadMod { + mark: PhantomData +} + +impl ThreadMod { + /// Gets a handle to the thread that invokes it. + pub fn current() -> Thread { + Thread { + pid: S::processor().current_pid(), + mark: PhantomData, + } + } + + /// Puts the current thread to sleep for the specified amount of time. + pub fn sleep(dur: Duration) { + let time = dur_to_ticks(dur); + info!("sleep: {:?} ticks", time); + let mut processor = S::processor(); + let pid = processor.current_pid(); + processor.sleep(pid, time); + processor.schedule(); + + fn dur_to_ticks(dur: Duration) -> usize { + return dur.as_secs() as usize * 100 + dur.subsec_nanos() as usize / 10_000_000; + } + } + + /// Spawns a new thread, returning a JoinHandle for it. + pub fn spawn(f: F) -> JoinHandle + where + F: Send + 'static + FnOnce() -> T, + T: Send + 'static, + { + info!("spawn:"); + let f = Box::into_raw(Box::new(f)); + let pid = S::processor().add(Context::new_kernel(kernel_thread_entry::, f as usize)); + return JoinHandle { + thread: Thread { pid, mark: PhantomData }, + mark: PhantomData, + }; + + extern fn kernel_thread_entry(f: usize) -> ! + where + S: ThreadSupport, + F: Send + 'static + FnOnce() -> T, + T: Send + 'static, + { + let f = unsafe { Box::from_raw(f as *mut F) }; + let ret = Box::new(f()); +// unsafe { LocalKey::::get_map() }.clear(); + let mut processor = S::processor(); + let pid = processor.current_pid(); + processor.exit(pid, Box::into_raw(ret) as usize); + processor.schedule(); + unreachable!() + } + } + + /// Cooperatively gives up a timeslice to the OS scheduler. + pub fn yield_now() { + info!("yield:"); + let mut processor = S::processor(); + processor.set_reschedule(); + processor.schedule(); + } + + /// Blocks unless or until the current thread's token is made available. + pub fn park() { + info!("park:"); + let mut processor = S::processor(); + let pid = processor.current_pid(); + processor.sleep_(pid); + processor.schedule(); + } +} + +/// A handle to a thread. +pub struct Thread { + pid: usize, + mark: PhantomData, +} + +impl Thread { + /// Atomically makes the handle's token available if it is not already. + pub fn unpark(&self) { + let mut processor = S::processor(); + 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 = S::processor(); + match processor.current_wait_for(self.thread.pid) { + WaitResult::Ok(_, exit_code) => unsafe { + Ok(*Box::from_raw(exit_code as *mut T)) + } + WaitResult::NotExist => Err(()), + } + } +} + +//pub struct LocalKey { +// init: fn() -> T, +//} +// +//impl LocalKey { +// pub fn with(&'static self, f: F) -> R +// where F: FnOnce(&T) -> R +// { +// let map = unsafe { Self::get_map() }; +// let key = self as *const _ as usize; +// if !map.contains_key(&key) { +// map.insert(key, Box::new((self.init)())); +// } +// let value = map.get(&key).unwrap().downcast_ref::().expect("type error"); +// f(value) +// } +// pub const fn new(init: fn() -> T) -> Self { +// LocalKey { init } +// } +// /// Get `BTreeMap>` at the current kernel stack bottom +// /// The stack must be aligned with 0x8000 +// unsafe fn get_map() -> &'static mut BTreeMap> { +// const STACK_SIZE: usize = 0x8000; +// let stack_var = 0usize; +// let ptr = (&stack_var as *const _ as usize) / STACK_SIZE * STACK_SIZE; +// let map = unsafe { &mut *(ptr as *mut Option>>) }; +// if map.is_none() { +// *map = Some(BTreeMap::new()); +// } +// map.as_mut().unwrap() +// } +//} +// +//pub mod test { +// use thread; +// use core::cell::RefCell; +// use core::time::Duration; +// +// 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(Duration::from_secs(2)); +// +// println!("Unpark the thread"); +// parked_thread.thread().unpark(); +// +// let ret = parked_thread.join().unwrap(); +// assert_eq!(ret, 5); +// } +// +// pub fn local_key() { +// static FOO: thread::LocalKey> = thread::LocalKey::new(|| RefCell::new(1)); +// +// FOO.with(|f| { +// assert_eq!(*f.borrow(), 1); +// *f.borrow_mut() = 2; +// }); +// +// // each thread starts out with the initial value of 1 +// thread::spawn(move || { +// FOO.with(|f| { +// assert_eq!(*f.borrow(), 1); +// *f.borrow_mut() = 3; +// }); +// }).join(); +// +// // we retain our original value of 2 despite the child thread +// FOO.with(|f| { +// assert_eq!(*f.borrow(), 2); +// }); +// println!("local key success"); +// } +//} diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 3682c25..cc3b6a0 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -64,7 +64,8 @@ mod consts; mod process; mod syscall; mod fs; -mod thread; + +use process::{thread, thread_}; mod sync; mod trap; mod console; diff --git a/kernel/src/process/context.rs b/kernel/src/process/context.rs index 13f0166..d4ebd96 100644 --- a/kernel/src/process/context.rs +++ b/kernel/src/process/context.rs @@ -15,10 +15,17 @@ impl ::ucore_process::processor::Context for Context { use core::mem::forget; forget(super::processor()); } + + fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Self { + let ms = MemorySet::new(); + Context { + arch: unsafe { ArchContext::new_kernel_thread(entry, arg, ms.kstack_top(), ms.token()) }, + memory_set: ms, + } + } } impl Context { - pub unsafe fn new_init() -> Self { Context { arch: ArchContext::null(), @@ -26,14 +33,6 @@ impl Context { } } - pub fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Self { - let ms = MemorySet::new(); - Context { - arch: unsafe { ArchContext::new_kernel_thread(entry, arg, ms.kstack_top(), ms.token()) }, - memory_set: ms, - } - } - /// Make a new user thread from ELF data pub fn new_user(data: &[u8]) -> Self { // Parse elf diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 84a86f5..6f207be 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -1,8 +1,9 @@ use spin::Once; use sync::{SpinNoIrqLock, Mutex, MutexGuard, SpinNoIrq}; pub use self::context::Context; -pub use ucore_process::processor::*; +pub use ucore_process::processor::{*, Context as _whatever}; pub use ucore_process::scheduler::*; +pub use ucore_process::thread::*; mod context; @@ -29,4 +30,23 @@ pub static PROCESSOR: Once> = Once::new(); pub fn processor() -> MutexGuard<'static, Processor, SpinNoIrq> { PROCESSOR.try().unwrap().lock() +} + +#[allow(non_camel_case_types)] +pub type thread = ThreadMod; + +pub mod thread_ { + pub type Thread = super::Thread; +} + +pub struct ThreadSupportImpl; + +impl ThreadSupport for ThreadSupportImpl { + type Context = Context; + type Scheduler = StrideScheduler; + type ProcessorGuard = MutexGuard<'static, Processor, SpinNoIrq>; + + fn processor() -> Self::ProcessorGuard { + processor() + } } \ No newline at end of file diff --git a/kernel/src/sync/condvar.rs b/kernel/src/sync/condvar.rs index 67dfe8c..186be0e 100644 --- a/kernel/src/sync/condvar.rs +++ b/kernel/src/sync/condvar.rs @@ -1,10 +1,11 @@ use alloc::VecDeque; use super::*; use thread; +use thread_; #[derive(Default)] pub struct Condvar { - wait_queue: SpinNoIrqLock>, + wait_queue: SpinNoIrqLock>, } impl Condvar { diff --git a/kernel/src/thread.rs b/kernel/src/thread.rs deleted file mode 100644 index f4968e6..0000000 --- a/kernel/src/thread.rs +++ /dev/null @@ -1,200 +0,0 @@ -//! Thread std-like interface -//! -//! Based on process mod. -//! Used in the kernel. - -use alloc::boxed::Box; -use alloc::BTreeMap; -use core::any::Any; -use core::marker::PhantomData; -use core::ptr; -use core::time::Duration; -use process::*; - -/// Gets a handle to the thread that invokes it. -pub fn current() -> Thread { - Thread { - pid: processor().current_pid(), - } -} - -/// Puts the current thread to sleep for the specified amount of time. -pub fn sleep(dur: Duration) { - let time = dur_to_ticks(dur); - info!("sleep: {:?} ticks", time); - let mut processor = processor(); - let pid = processor.current_pid(); - processor.sleep(pid, time); - processor.schedule(); - - fn dur_to_ticks(dur: Duration) -> usize { - return dur.as_secs() as usize * 100 + dur.subsec_nanos() as usize / 10_000_000; - } -} - -/// Spawns a new thread, returning a JoinHandle for it. -pub fn spawn(f: F) -> JoinHandle - where - F: Send + 'static + FnOnce() -> T, - T: Send + 'static, -{ - info!("spawn:"); - let f = Box::into_raw(Box::new(f)); - let pid = processor().add(Context::new_kernel(kernel_thread_entry::, f 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, - { - let f = unsafe { Box::from_raw(f as *mut F) }; - let ret = Box::new(f()); - unsafe { LocalKey::::get_map() }.clear(); - let mut processor = processor(); - let pid = processor.current_pid(); - processor.exit(pid, Box::into_raw(ret) as usize); - processor.schedule(); - unreachable!() - } -} - -/// Cooperatively gives up a timeslice to the OS scheduler. -pub fn yield_now() { - info!("yield:"); - let mut processor = processor(); - processor.set_reschedule(); - processor.schedule(); -} - -/// Blocks unless or until the current thread's token is made available. -pub fn park() { - info!("park:"); - let mut processor = processor(); - 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(); - 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(); - match processor.current_wait_for(self.thread.pid) { - WaitResult::Ok(_, exit_code) => unsafe { - Ok(*Box::from_raw(exit_code as *mut T)) - } - WaitResult::NotExist => Err(()), - } - } -} - -pub struct LocalKey { - init: fn() -> T, -} - -impl LocalKey { - pub fn with(&'static self, f: F) -> R - where F: FnOnce(&T) -> R - { - let map = unsafe { Self::get_map() }; - let key = self as *const _ as usize; - if !map.contains_key(&key) { - map.insert(key, Box::new((self.init)())); - } - let value = map.get(&key).unwrap().downcast_ref::().expect("type error"); - f(value) - } - pub const fn new(init: fn() -> T) -> Self { - LocalKey { init } - } - /// Get `BTreeMap>` at the current kernel stack bottom - /// The stack must be aligned with 0x8000 - unsafe fn get_map() -> &'static mut BTreeMap> { - const STACK_SIZE: usize = 0x8000; - let stack_var = 0usize; - let ptr = (&stack_var as *const _ as usize) / STACK_SIZE * STACK_SIZE; - let map = unsafe { &mut *(ptr as *mut Option>>) }; - if map.is_none() { - *map = Some(BTreeMap::new()); - } - map.as_mut().unwrap() - } -} - -pub mod test { - use thread; - use core::cell::RefCell; - use core::time::Duration; - - 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(Duration::from_secs(2)); - - println!("Unpark the thread"); - parked_thread.thread().unpark(); - - let ret = parked_thread.join().unwrap(); - assert_eq!(ret, 5); - } - - pub fn local_key() { - static FOO: thread::LocalKey> = thread::LocalKey::new(|| RefCell::new(1)); - - FOO.with(|f| { - assert_eq!(*f.borrow(), 1); - *f.borrow_mut() = 2; - }); - - // each thread starts out with the initial value of 1 - thread::spawn(move || { - FOO.with(|f| { - assert_eq!(*f.borrow(), 1); - *f.borrow_mut() = 3; - }); - }).join(); - - // we retain our original value of 2 despite the child thread - FOO.with(|f| { - assert_eq!(*f.borrow(), 2); - }); - println!("local key success"); - } -}