Building a stupid Mutex in the Rust

Firstly let’s understand what’s mutexes do

Let’s create a stupid mutex

use std::sync::atomic::{AtomicBool, Ordering};#[derive(Debug, Default)]
struct Mutex {
is_acquired: AtomicBool,
}
impl Mutex {
fn acquire(&self) {
while !self.is_acquired.swap(true, Ordering::AcqRel) {
// wait
}
}

fn release(&self) {
self.is_acquired.store(false, Ordering::Release);
}
}
  • The while the loop will use CPU resources in idle. We need somebody like yield instruction, which tells your OS about the current thread may be stopped for some time.
  • We cannot get mutable access to our data. But we have unique access when the mutex is acquiring. Therefore we can use DerefMut trait to get mutable access to protected data.

Let’s signal the processor that it is inside a spin-loop

use std::sync::atomic::{AtomicBool, Ordering, spin_loop_hint};#[derive(Debug, Default)]
struct Mutex {
is_acquired: AtomicBool,
}
impl Mutex {
fn acquire(&self) {
while !self.is_acquired.swap(true, Ordering::AcqRel) {
spin_loop_hint() // Now we signals the processor that it is inside a busy-wait spin-loop
}
}

fn release(&self) {
self.is_acquired.store(false, Ordering::Release);
}
}

Making smart pointer from our stupid Mutex

use std::sync::atomic::{AtomicBool, Ordering, spin_loop_hint};
use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
struct Mutex<T> {
is_acquired: AtomicBool,
data: UnsafeCell<T>,
}
impl<T> Mutex<T> {
fn new(data: T) -> Mutex<T> {
Mutex {
is_acquired: AtomicBool::default(),
data: UnsafeCell::new(data),
}
}
fn acquire(&self) -> MutexGuard<'_, T> {
while !self.is_acquired.swap(true, Ordering::AcqRel) {
spin_loop_hint() // Now we signals the processor that it is inside a busy-wait spin-loop
}
MutexGuard { mutex: &self }
}
fn release(&self) {
self.is_acquired.store(false, Ordering::Release);
}
}
struct MutexGuard<'a, T> {
mutex: &'a Mutex<T>,
}
impl<T> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.mutex.data.get() }
}
}
impl<T> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.mutex.data.get() }
}
}
impl<T> Drop for MutexGuard<'_, T> {
fn drop(&mut self) {
self.mutex.release()
}
}
// We need to force Send and Sync traits because our mutex has
// UnsafeCell, which don't realize it
// As long as T: Send, it's fine to send and share Mutex<T> between threads.
unsafe impl<T> Send for Mutex<T> where T: Send {}
unsafe impl<T> Sync for Mutex<T> where T: Send {}
unsafe impl<T> Send for MutexGuard<'_, T> where T: Send {}
unsafe impl<T> Sync for MutexGuard<'_, T> where T: Send + Sync {}

Let’s write the test

use std::thread::spawn;
use std::sync::Arc;
#[test]
fn test() {
let num = 100;
let mutex = Arc::new(Mutex::new(0));
let ths: Vec<_> = (0..num)
.map(|_| {
let mutex = mutex.clone();
spawn(move || {
let mut lock = mutex.acquire();
*lock += 1;
})
})
.collect();
for thread in ths {
thread.join().unwrap();
}

let lock = mutex.acquire();

assert_eq!(*lock, num)
}

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store