From 71e49e3959397b4d2341db471e03dac07ecbf657 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Thu, 31 May 2018 22:22:19 +0800 Subject: [PATCH] MutexSupport framework --- src/process/mod.rs | 11 +++-- src/sync.rs | 116 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 96 insertions(+), 31 deletions(-) diff --git a/src/process/mod.rs b/src/process/mod.rs index b1e2bb7..cd69703 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -1,6 +1,6 @@ use memory::MemoryController; use spin::Once; -use sync::Mutex; +use sync::SpinNoIrqLock; use core::slice; use alloc::String; @@ -13,7 +13,8 @@ mod scheduler; pub fn init(mut mc: MemoryController) { - PROCESSOR.call_once(|| {Mutex::new({ + PROCESSOR.call_once(|| { + SpinNoIrqLock::new({ let initproc = Process::new_init(&mut mc); let idleproc = Process::new("idle", idle_thread, 0, &mut mc); let mut processor = Processor::new(); @@ -21,11 +22,11 @@ pub fn init(mut mc: MemoryController) { processor.add(idleproc); processor })}); - MC.call_once(|| Mutex::new(mc)); + MC.call_once(|| SpinNoIrqLock::new(mc)); } -pub static PROCESSOR: Once> = Once::new(); -pub static MC: Once> = Once::new(); +pub static PROCESSOR: Once> = Once::new(); +pub static MC: Once> = Once::new(); extern fn idle_thread(arg: usize) -> ! { loop { diff --git a/src/sync.rs b/src/sync.rs index 9eda90e..2c89a87 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -1,37 +1,42 @@ -//! Spin & no-interrupt lock +//! Mutex (Spin, Spin-NoInterrupt, Yield) //! //! Modified from spin::mutex. -//! Search 'interrupt::' for difference. 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; + /// Spin & no-interrupt lock -pub struct Mutex +pub struct Mutex { lock: AtomicBool, + support: PhantomData, 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> +pub struct MutexGuard<'a, T: ?Sized + 'a, S: MutexSupport> { lock: &'a AtomicBool, data: &'a mut T, - flags: usize, + support: S, } // Same unsafe impls as `std::sync::Mutex` -unsafe impl Sync for Mutex {} +unsafe impl Sync for Mutex {} -unsafe impl Send for Mutex {} +unsafe impl Send for Mutex {} -impl Mutex +impl Mutex { /// Creates a new spinlock wrapping the supplied data. /// @@ -49,10 +54,11 @@ impl Mutex /// drop(lock); /// } /// ``` - pub const fn new(user_data: T) -> Mutex { + pub const fn new(user_data: T) -> Mutex { Mutex { lock: ATOMIC_BOOL_INIT, data: UnsafeCell::new(user_data), + support: PhantomData, } } @@ -65,13 +71,13 @@ impl Mutex } } -impl Mutex +impl Mutex { fn obtain_lock(&self) { 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) { - unsafe { asm!("pause" :::: "volatile"); } + S::cpu_relax(); } } } @@ -91,14 +97,14 @@ impl Mutex /// } /// /// ``` - pub fn lock(&self) -> MutexGuard + pub fn lock(&self) -> MutexGuard { - let flags = unsafe { interrupt::disable_and_store() }; + let support = S::before_lock(); self.obtain_lock(); MutexGuard { lock: &self.lock, data: unsafe { &mut *self.data.get() }, - flags, + support, } } @@ -115,53 +121,111 @@ 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 flags = unsafe { interrupt::disable_and_store() }; + pub fn try_lock(&self) -> Option> { + let support = 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() }, - flags, + support, }) } else { - unsafe { interrupt::restore(flags) }; + 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: {:?} }}", &*guard), - None => write!(f, "Mutex {{ }}"), + Some(guard) => write!(f, "Mutex<{:?}> {{ data: {:?} }}", self.support, &*guard), + None => write!(f, "Mutex<{:?}> {{ }}", self.support), } } } -impl Default for Mutex { - fn default() -> Mutex { +impl Default for Mutex { + fn default() -> Mutex { Mutex::new(Default::default()) } } -impl<'a, T: ?Sized> Deref for MutexGuard<'a, T> +impl<'a, T: ?Sized, S: MutexSupport> Deref for MutexGuard<'a, T, S> { type Target = T; fn deref<'b>(&'b self) -> &'b T { &*self.data } } -impl<'a, T: ?Sized> DerefMut for MutexGuard<'a, T> +impl<'a, T: ?Sized, S: MutexSupport> DerefMut for MutexGuard<'a, T, S> { fn deref_mut<'b>(&'b mut self) -> &'b mut T { &mut *self.data } } -impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> +impl<'a, T: ?Sized, S: MutexSupport> Drop for MutexGuard<'a, T, S> { /// The dropping of the MutexGuard will release the lock it was created from. fn drop(&mut self) { self.lock.store(false, Ordering::Release); + self.support.after_unlock(); + } +} + +/// Low-level support for mutex +pub trait MutexSupport { + /// Called when failing to acquire the lock + fn cpu_relax(); + /// Called before lock() & try_lock() + fn before_lock() -> Self; + /// Called when MutexGuard dropping & try_lock() failed + fn after_unlock(&self); +} + +/// Spin lock +pub struct Spin; + +impl MutexSupport for Spin { + fn cpu_relax() { + unsafe { asm!("pause" :::: "volatile"); } + } + fn before_lock() -> Self { + Spin + } + fn after_unlock(&self) {} +} + +/// Spin & no-interrupt lock +pub struct SpinNoIrq { + flags: usize, +} + +impl MutexSupport for SpinNoIrq { + fn cpu_relax() { + unsafe { asm!("pause" :::: "volatile"); } + } + fn before_lock() -> Self { + SpinNoIrq { + flags: unsafe { interrupt::disable_and_store() }, + } + } + fn after_unlock(&self) { unsafe { interrupt::restore(self.flags) }; } +} + +/// With thread support +pub struct Yield; + +impl MutexSupport for Yield { + fn cpu_relax() { + use thread; + thread::yield_now(); + } + fn before_lock() -> Self { + unimplemented!() + } + fn after_unlock(&self) { + unimplemented!() + } } \ No newline at end of file