1
0
mirror of https://github.com/rcore-os/rCore.git synced 2024-11-23 00:16:17 +04:00

Move thread mod to ucore-process crate

This commit is contained in:
WangRunji 2018-07-17 19:06:30 +08:00
parent fed7b38845
commit a2111a928f
8 changed files with 280 additions and 214 deletions

View File

@ -15,5 +15,6 @@ extern crate std;
pub mod processor;
pub mod scheduler;
pub mod thread;
mod util;
mod event_hub;

View File

@ -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_<T: Context, S: Scheduler> {

243
crate/process/src/thread.rs Normal file
View File

@ -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<ThreadSupportImpl>;
//! ```
//!
//! ```
//! // 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<Target=Processor_<Self::Context, Self::Scheduler>>;
fn processor() -> Self::ProcessorGuard;
}
/// Root structure served as thread mod
pub struct ThreadMod<S: ThreadSupport> {
mark: PhantomData<S>
}
impl<S: ThreadSupport> ThreadMod<S> {
/// Gets a handle to the thread that invokes it.
pub fn current() -> Thread<S> {
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, T>(f: F) -> JoinHandle<S, T>
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::<S, F, T>, f as usize));
return JoinHandle {
thread: Thread { pid, mark: PhantomData },
mark: PhantomData,
};
extern fn kernel_thread_entry<S, F, T>(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::<usize>::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<S: ThreadSupport> {
pid: usize,
mark: PhantomData<S>,
}
impl<S: ThreadSupport> Thread<S> {
/// 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<S: ThreadSupport, T> {
thread: Thread<S>,
mark: PhantomData<T>,
}
impl<S: ThreadSupport, T> JoinHandle<S, T> {
/// Extracts a handle to the underlying thread.
pub fn thread(&self) -> &Thread<S> {
&self.thread
}
/// Waits for the associated thread to finish.
pub fn join(self) -> Result<T, ()> {
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<T: 'static> {
// init: fn() -> T,
//}
//
//impl<T: 'static> LocalKey<T> {
// pub fn with<F, R>(&'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::<T>().expect("type error");
// f(value)
// }
// pub const fn new(init: fn() -> T) -> Self {
// LocalKey { init }
// }
// /// Get `BTreeMap<usize, Box<Any>>` at the current kernel stack bottom
// /// The stack must be aligned with 0x8000
// unsafe fn get_map() -> &'static mut BTreeMap<usize, Box<Any>> {
// 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<BTreeMap<usize, Box<Any>>>) };
// 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<RefCell<usize>> = 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");
// }
//}

View File

@ -64,7 +64,8 @@ mod consts;
mod process;
mod syscall;
mod fs;
mod thread;
use process::{thread, thread_};
mod sync;
mod trap;
mod console;

View File

@ -15,24 +15,23 @@ impl ::ucore_process::processor::Context for Context {
use core::mem::forget;
forget(super::processor());
}
}
impl Context {
pub unsafe fn new_init() -> Self {
Context {
arch: ArchContext::null(),
memory_set: MemorySet::new(),
}
}
pub fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Self {
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(),
memory_set: MemorySet::new(),
}
}
/// Make a new user thread from ELF data
pub fn new_user(data: &[u8]) -> Self {

View File

@ -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<SpinNoIrqLock<Processor>> = Once::new();
pub fn processor() -> MutexGuard<'static, Processor, SpinNoIrq> {
PROCESSOR.try().unwrap().lock()
}
#[allow(non_camel_case_types)]
pub type thread = ThreadMod<ThreadSupportImpl>;
pub mod thread_ {
pub type Thread = super::Thread<super::ThreadSupportImpl>;
}
pub struct ThreadSupportImpl;
impl ThreadSupport for ThreadSupportImpl {
type Context = Context;
type Scheduler = StrideScheduler;
type ProcessorGuard = MutexGuard<'static, Processor, SpinNoIrq>;
fn processor() -> Self::ProcessorGuard {
processor()
}
}

View File

@ -1,10 +1,11 @@
use alloc::VecDeque;
use super::*;
use thread;
use thread_;
#[derive(Default)]
pub struct Condvar {
wait_queue: SpinNoIrqLock<VecDeque<thread::Thread>>,
wait_queue: SpinNoIrqLock<VecDeque<thread_::Thread>>,
}
impl Condvar {

View File

@ -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, T>(f: F) -> JoinHandle<T>
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, T>, f as usize));
return JoinHandle {
thread: Thread { pid },
mark: PhantomData,
};
extern fn kernel_thread_entry<F, T>(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::<usize>::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<T> {
thread: Thread,
mark: PhantomData<T>,
}
impl<T> JoinHandle<T> {
/// 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<T, ()> {
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<T: 'static> {
init: fn() -> T,
}
impl<T: 'static> LocalKey<T> {
pub fn with<F, R>(&'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::<T>().expect("type error");
f(value)
}
pub const fn new(init: fn() -> T) -> Self {
LocalKey { init }
}
/// Get `BTreeMap<usize, Box<Any>>` at the current kernel stack bottom
/// The stack must be aligned with 0x8000
unsafe fn get_map() -> &'static mut BTreeMap<usize, Box<Any>> {
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<BTreeMap<usize, Box<Any>>>) };
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<RefCell<usize>> = 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");
}
}