From 93293452898164f17bd9ad33afd283f3220afff1 Mon Sep 17 00:00:00 2001 From: python-ast-person <102675344+python-ast-person@users.noreply.github.com> Date: Tue, 9 May 2023 16:49:13 +0100 Subject: [PATCH 1/4] Create mutex-locking-and-unlocking.md initial version of "Create mutex-locking-and-unlocking.md" --- src/arc-mutex/mutex-locking-and-unlocking.md | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/arc-mutex/mutex-locking-and-unlocking.md diff --git a/src/arc-mutex/mutex-locking-and-unlocking.md b/src/arc-mutex/mutex-locking-and-unlocking.md new file mode 100644 index 00000000..b7e9db17 --- /dev/null +++ b/src/arc-mutex/mutex-locking-and-unlocking.md @@ -0,0 +1,37 @@ + + +# Locking and Unlocking +Locking and unlocking is simple with our layout; Make sure the mutex is in the correct state, then toggle it. + + use core::sync::fence; + + impl Mutex{ + pub fn lock(&self)->MutexGuard{ + while self.is_locked.compare_exchange( + false,//expecting it to not be locked + true ,//lock it if it is not already locked + Ordering::Acquire,//If we succeed, use acquire ordering to prevent + Ordering::Release//If we faill to acquire the lock, it does not matter which ordering we choose, so choose the fastest. + ).is_err(){}//If we fail to aquire the lock immediately try again. + + fence(Ordering::Acquire);//prevent writes to the data protected by the mutex from being reordered before the mutex is actually acquired. + //safety:The previous steps we took means that no one else has the lock + unsafe{return &mut self.value.get()} + } + + ///Safety:Make sure the caller has acquired the lock, and will not dereference the return mutable reference again. + pub unsafe fn unlock(&self){ + if self.is_locked.compare_exchange( + true,//The caller should ensure the mutex is locked + false,//Mark the lock as unlocked + Ordering::Release, + Ordering::Relaxed,//We are about to panic anyway, the ordering doesn't matter + ).is_err{ + panic!("Unlock called while the lock was not locked") + }; + } + } + + + +> Written with [StackEdit](https://stackedit.io/). From b5e582f58d670fa1b9337b7518415518776695ee Mon Sep 17 00:00:00 2001 From: python-ast-person <102675344+python-ast-person@users.noreply.github.com> Date: Tue, 9 May 2023 16:56:07 +0100 Subject: [PATCH 2/4] Create mutex-adding--RAII--suport.md initial version of "mutex-adding--RAII--suport.md". --- src/arc-mutex/mutex-adding--RAII--suport.md | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/arc-mutex/mutex-adding--RAII--suport.md diff --git a/src/arc-mutex/mutex-adding--RAII--suport.md b/src/arc-mutex/mutex-adding--RAII--suport.md new file mode 100644 index 00000000..d6a028e8 --- /dev/null +++ b/src/arc-mutex/mutex-adding--RAII--suport.md @@ -0,0 +1,47 @@ +# Adding RAII Support +While the version we made in the last chapter is usable, it requires unsafe code on the part of the user to unlock it and doesn't the user from using the mutable reference after the lock has been freed. We can solve these problems by using a RAII guard. + +The guard itself will use the `core::ops::Deref` trait to allow it to still be used as if it were a mutable reference and the `Drop` trait to ensure that the mutex is unlocked when it leaves scope. + +First we implement `Deref` and `DerefMut`: + + use core::ops::{Deref,MutDeref}; + + struct MutexGuard<'a,T>(&'a Mutex); + + impl<'a,T> Deref for MutexGuard<'a,T>{ + type Target = T; + fn deref(&self)->&T{ + unsafe{&*self.0.value.get()} + } + } + + impl<'a,T> DerefMut for MutexGuard<'a,T>{ + + +Then add a `Drop` implementation, to unlock the mutex when it leaves scope: + + impl<'a,T> Drop for MutexGuard<'a,T>{ + fn drop(&mut self){ + unsafe{self.0.unlock()} + } + } + +Finally, we need to change our `lock()` method to return a mutex guard instead of a raw mutable reference (this should replace the existing lock method on mutex): + + impl Mutex{ + pub fn lock(&self)->MutexGuard{ + while self.is_locked.compare_exchange( + false,//expecting it to not be locked + true ,//lock it if it is not already locked + Ordering::Acquire,//If we succeed, use acquire ordering to prevent + Ordering::Release//If we faill to acquire the lock, it does not matter which ordering we choose, so choose the fastest. + ).is_err(){}//If we fail to aquire the lock immediately try again. + + fence(Ordering::Acquire);//prevent writes to the data protected by the mutex + //from being reordered before the mutex is actually acquired. + return MutexGuard(self); + } + } + +> Written with [StackEdit](https://stackedit.io/). From f40087cbfa972825e8d1c7ec6f5ca3dff0fce949 Mon Sep 17 00:00:00 2001 From: python-ast-person <102675344+python-ast-person@users.noreply.github.com> Date: Tue, 9 May 2023 16:56:56 +0100 Subject: [PATCH 3/4] Update mutex-locking-and-unlocking.md Add missing memory fence. --- src/arc-mutex/mutex-locking-and-unlocking.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/arc-mutex/mutex-locking-and-unlocking.md b/src/arc-mutex/mutex-locking-and-unlocking.md index b7e9db17..5e471227 100644 --- a/src/arc-mutex/mutex-locking-and-unlocking.md +++ b/src/arc-mutex/mutex-locking-and-unlocking.md @@ -29,6 +29,9 @@ Locking and unlocking is simple with our layout; Make sure the mutex is in the c ).is_err{ panic!("Unlock called while the lock was not locked") }; + + fence(Ordering::Acquire);//prevent writes to the data protected by the mutex + //from being reordered before the mutex is actually acquired. } } From f323bdc7c5a774d2a5ae1e6a2ccd17998fa97c35 Mon Sep 17 00:00:00 2001 From: python-ast-person <102675344+python-ast-person@users.noreply.github.com> Date: Tue, 9 May 2023 16:58:15 +0100 Subject: [PATCH 4/4] Create mutex-layout-and-boiler-plate.md initial version of "mutex-layout-and-boiler-plate.md". --- .../mutex-layout-and-boiler-plate.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/arc-mutex/mutex-layout-and-boiler-plate.md diff --git a/src/arc-mutex/mutex-layout-and-boiler-plate.md b/src/arc-mutex/mutex-layout-and-boiler-plate.md new file mode 100644 index 00000000..1d610458 --- /dev/null +++ b/src/arc-mutex/mutex-layout-and-boiler-plate.md @@ -0,0 +1,31 @@ +# Layout & Boilerplate +First we should make a layout for our mutex. The minimum would be a Boolean to store if the mutex is locked or not and an Unsafe Cell to store the actual value inside the mutex. For now, we can use the following layout: + + use core::cell::UnsafeCell; + use core::sync::atomic::{AtomicBool,Ordering}; + + struct Mutex{ + is_locked:AtomicBool, + value:UnsafeCell, + } + +We also should fill out some boilerplate: + + impl Mutex{ + pub fn new(value:T)->Self{ + Self{is_locked:AtomicBool::new(false),UnsafeCell::new(value)} + } + + pub fn get_mut(&mut self)->&mut T{ + self.value.get_mut() + } + + pub fn into_inner(self)->T{ + self.value.into_inner() + } + } +And a sync impl: + + impl Sync for Mutex{} + +> Written with [StackEdit](https://stackedit.io/).