From 31bc92aec658c66f6279a9c721dd62485c6ce1ba Mon Sep 17 00:00:00 2001 From: WangRunji Date: Fri, 1 Jun 2018 11:47:58 +0800 Subject: [PATCH] ThreadLock. Dining philosophers problem. Fix thread::spawn. --- src/lib.rs | 3 + src/sync.rs | 185 +++++++++++++++++++++++++++++++++++++------------ src/syscall.rs | 3 +- src/thread.rs | 21 ++++-- 4 files changed, 161 insertions(+), 51 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c477e2e..eaa2938 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,9 @@ pub extern "C" fn rust_main(multiboot_information_address: usize) -> ! { unsafe{ arch::interrupt::enable(); } +// thread::test::unpack(); + sync::philosopher::philosopher(); + // 直接进入用户态暂不可用:内核代码用户不可访问 // unsafe{ // use arch::syscall; diff --git a/src/sync.rs b/src/sync.rs index 2c89a87..996116e 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -1,4 +1,4 @@ -//! Mutex (Spin, Spin-NoInterrupt, Yield) +//! Mutex (Spin, SpinNoIrq, Thread) //! //! Modified from spin::mutex. @@ -6,29 +6,28 @@ use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; use core::cell::UnsafeCell; use core::ops::{Deref, DerefMut}; use core::fmt; -use core::marker::PhantomData; use arch::interrupt; pub type SpinLock = Mutex; pub type SpinNoIrqLock = Mutex; -pub type YieldLock = Mutex; +pub type ThreadLock = Mutex; -/// Spin & no-interrupt lock pub struct Mutex { lock: AtomicBool, - support: PhantomData, + support: S, data: UnsafeCell, } /// A guard to which the protected data can be accessed /// /// When the guard falls out of scope it will release the lock. -pub struct MutexGuard<'a, T: ?Sized + 'a, S: MutexSupport> +pub struct MutexGuard<'a, T: ?Sized + 'a, S: MutexSupport + 'a> { lock: &'a AtomicBool, data: &'a mut T, - support: S, + support: &'a S, + support_guard: S::GuardData, } // Same unsafe impls as `std::sync::Mutex` @@ -54,11 +53,11 @@ impl Mutex /// drop(lock); /// } /// ``` - pub const fn new(user_data: T) -> Mutex { + pub fn new(user_data: T) -> Mutex { Mutex { lock: ATOMIC_BOOL_INIT, data: UnsafeCell::new(user_data), - support: PhantomData, + support: S::new(), } } @@ -77,7 +76,7 @@ impl Mutex while self.lock.compare_and_swap(false, true, Ordering::Acquire) != false { // Wait until the lock looks unlocked before retrying while self.lock.load(Ordering::Relaxed) { - S::cpu_relax(); + self.support.cpu_relax(); } } } @@ -99,12 +98,13 @@ impl Mutex /// ``` pub fn lock(&self) -> MutexGuard { - let support = S::before_lock(); + let support_guard = S::before_lock(); self.obtain_lock(); MutexGuard { lock: &self.lock, data: unsafe { &mut *self.data.get() }, - support, + support: &self.support, + support_guard, } } @@ -122,26 +122,26 @@ impl Mutex /// Tries to lock the mutex. If it is already locked, it will return None. Otherwise it returns /// a guard within Some. pub fn try_lock(&self) -> Option> { - let support = S::before_lock(); + let support_guard = S::before_lock(); if self.lock.compare_and_swap(false, true, Ordering::Acquire) == false { Some(MutexGuard { lock: &self.lock, data: unsafe { &mut *self.data.get() }, - support, + support: &self.support, + support_guard, }) } else { - support.after_unlock(); None } } } -impl fmt::Debug for Mutex +impl fmt::Debug for Mutex { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.try_lock() { - Some(guard) => write!(f, "Mutex<{:?}> {{ data: {:?} }}", self.support, &*guard), - None => write!(f, "Mutex<{:?}> {{ }}", self.support), + Some(guard) => write!(f, "Mutex {{ data: {:?}, support: {:?} }}", &*guard, self.support), + None => write!(f, "Mutex {{ , support: {:?} }}", self.support), } } } @@ -174,58 +174,157 @@ impl<'a, T: ?Sized, S: MutexSupport> Drop for MutexGuard<'a, T, S> /// Low-level support for mutex pub trait MutexSupport { + type GuardData; + fn new() -> Self; /// Called when failing to acquire the lock - fn cpu_relax(); + fn cpu_relax(&self); /// Called before lock() & try_lock() - fn before_lock() -> Self; - /// Called when MutexGuard dropping & try_lock() failed + fn before_lock() -> Self::GuardData; + /// Called when MutexGuard dropping fn after_unlock(&self); } /// Spin lock +#[derive(Debug)] pub struct Spin; impl MutexSupport for Spin { - fn cpu_relax() { + type GuardData = (); + + fn new() -> Self { Spin } + fn cpu_relax(&self) { unsafe { asm!("pause" :::: "volatile"); } } - fn before_lock() -> Self { - Spin - } + fn before_lock() -> Self::GuardData {} fn after_unlock(&self) {} } /// Spin & no-interrupt lock -pub struct SpinNoIrq { - flags: usize, +#[derive(Debug)] +pub struct SpinNoIrq; + +/// Contains RFLAGS before disable interrupt, will auto restore it when dropping +pub struct FlagsGuard(usize); + +impl Drop for FlagsGuard { + fn drop(&mut self) { + unsafe { interrupt::restore(self.0) }; + } } impl MutexSupport for SpinNoIrq { - fn cpu_relax() { - unsafe { asm!("pause" :::: "volatile"); } + type GuardData = FlagsGuard; + fn new() -> Self { + SpinNoIrq } - fn before_lock() -> Self { - SpinNoIrq { - flags: unsafe { interrupt::disable_and_store() }, - } + fn cpu_relax(&self) { + unsafe { asm!("pause" :::: "volatile"); } } - fn after_unlock(&self) { - unsafe { interrupt::restore(self.flags) }; + fn before_lock() -> Self::GuardData { + FlagsGuard(unsafe { interrupt::disable_and_store() }) } + fn after_unlock(&self) {} } +use thread; +use alloc::VecDeque; + /// With thread support -pub struct Yield; +pub struct Thread { + wait_queue: SpinLock>, +} -impl MutexSupport for Yield { - fn cpu_relax() { - use thread; - thread::yield_now(); +impl MutexSupport for Thread { + type GuardData = (); + fn new() -> Self { + Thread { wait_queue: SpinLock::new(VecDeque::new()) } } - fn before_lock() -> Self { - unimplemented!() + fn cpu_relax(&self) { + self.wait_queue.lock().push_back(thread::current()); + thread::park(); } + fn before_lock() -> Self::GuardData {} fn after_unlock(&self) { - unimplemented!() + if let Some(t) = self.wait_queue.lock().pop_front() { + t.unpark(); + } + } +} + + +pub mod philosopher { + use thread; + use core::time::Duration; + use alloc::{arc::Arc, Vec}; + use super::ThreadLock as Mutex; + + struct Philosopher { + name: &'static str, + left: usize, + right: usize, + } + + impl Philosopher { + fn new(name: &'static str, left: usize, right: usize) -> Philosopher { + Philosopher { + name, + left, + right, + } + } + + fn eat(&self, table: &Table) { + let _left = table.forks[self.left].lock(); + let _right = table.forks[self.right].lock(); + + println!("{} is eating.", self.name); + thread::sleep(Duration::from_secs(1)); + } + + fn think(&self) { + println!("{} is thinking.", self.name); + thread::sleep(Duration::from_secs(1)); + } + } + + struct Table { + forks: Vec>, + } + + pub fn philosopher() { + let table = Arc::new(Table { + forks: vec![ + Mutex::new(()), + Mutex::new(()), + Mutex::new(()), + Mutex::new(()), + Mutex::new(()), + ] + }); + + let philosophers = vec![ + Philosopher::new("1", 0, 1), + Philosopher::new("2", 1, 2), + Philosopher::new("3", 2, 3), + Philosopher::new("4", 3, 4), + Philosopher::new("5", 0, 4), + ]; + + let handles: Vec<_> = philosophers.into_iter().map(|p| { + let table = table.clone(); + + thread::spawn(move || { + for i in 0..5 { + p.think(); + p.eat(&table); + println!("{} iter {} end.", p.name, i); + } + }) + }).collect(); + + for h in handles { + h.join().unwrap(); + } + println!("philosophers dining end"); } } \ No newline at end of file diff --git a/src/syscall.rs b/src/syscall.rs index 069f662..2e486d9 100644 --- a/src/syscall.rs +++ b/src/syscall.rs @@ -133,7 +133,8 @@ fn sys_exit(error_code: usize) -> i32 { } fn sys_sleep(time: usize) -> i32 { - thread::sleep(time); + use core::time::Duration; + thread::sleep(Duration::from_millis(time as u64 * 10)); 0 } diff --git a/src/thread.rs b/src/thread.rs index 1c25cf9..0ad3151 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -6,6 +6,7 @@ use process::*; use core::marker::PhantomData; use core::ptr; +use core::time::Duration; use alloc::boxed::Box; /// Gets a handle to the thread that invokes it. @@ -16,13 +17,16 @@ pub fn current() -> Thread { } /// 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); +pub fn sleep(dur: Duration) { + info!("sleep: {:?}", dur); let mut processor = PROCESSOR.try().unwrap().lock(); let pid = processor.current_pid(); - processor.sleep(pid, time); + processor.sleep(pid, dur_to_ticks(dur)); 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. @@ -33,7 +37,8 @@ pub fn spawn(f: F) -> JoinHandle { info!("spawn:"); use process; - let pid = process::add_kernel_process(kernel_thread_entry::, &f as *const _ as usize); + let f = Box::leak(Box::new(f)); + let pid = process::add_kernel_process(kernel_thread_entry::, f as *mut _ as usize); return JoinHandle { thread: Thread { pid }, mark: PhantomData, @@ -44,11 +49,12 @@ pub fn spawn(f: F) -> JoinHandle F: Send + 'static + FnOnce() -> T, T: Send + 'static, { - let f = unsafe { ptr::read(f as *mut F) }; + let f = unsafe { Box::from_raw(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); + processor.schedule(); unreachable!() } } @@ -115,6 +121,7 @@ impl JoinHandle { pub mod test { use thread; + use core::time::Duration; pub fn unpack() { let parked_thread = thread::spawn(|| { @@ -125,7 +132,7 @@ pub mod test { }); // Let some time pass for the thread to be spawned. - thread::sleep(200); + thread::sleep(Duration::from_secs(2)); println!("Unpark the thread"); parked_thread.thread().unpark();