Ad

Write your own spinlock to practice your rust skill.

You might need to write some unsafe blocks, or you can use UnsafeCell or other tools to avoid using unsafe.

Note: Since the test is a parallel adder of i32, you should at least impl Sync for SpinLock<i32> like what I did in the demo.

use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::*;

pub struct SpinLock<T> {
    locked: AtomicBool,
    value: *mut T,
}

impl<T> SpinLock<T> {
    pub fn new(value: T) -> Self {
        Self {
            locked: AtomicBool::new(false),
            value: Box::leak(Box::new(value)),
        }
    }

    // use this macro to suppress the clippy error
    #[allow(clippy::mut_from_ref)]
    pub fn lock(&self) -> &mut T {
        while self
            .locked
            .compare_exchange_weak(false, true, Acquire, Relaxed)
            .is_err()
        {
            std::hint::spin_loop();
        }
        unsafe { &mut *self.value }
    }

    pub fn unlock(&self) {
        self.locked.store(false, Release);
    }
}

impl<T> Drop for SpinLock<T> {
    fn drop(&mut self) {
        unsafe {
            let _ = Box::from_raw(self.value);
        }
    }
}

// mark `Sync` explicitly for i32
unsafe impl Sync for SpinLock<i32> {}

Assume we have a customized struct in rust, and it holds a vector inside.

How can we modify the vector inside the struct?

The easist way to achieve the target is using &mut self, can you provide other solutions?

struct VecHolder {
    vec_: Vec<i32>,
}

impl VecHolder {
    fn new() -> Self {
        VecHolder { vec_: Vec::new() }
    }

    // NOTE: use `&mut self` instead of `&self` to mutate the inner `Vec`.
    fn push(&mut self, i: i32) {
        self.vec_.push(i);
    }
    
    fn pop(&mut self) -> Option<i32> {
        self.vec_.pop()
    }
}

Use std::sync::mpsc to achieve the target.
Ignore the bad performance, just for fun~

Code
Diff
  • use std::{sync::mpsc, thread};
    
    fn count() -> u32 {
        let (tx, rx) = mpsc::channel();
        let mut count = 0_u32;
    
        thread::scope(|s| {
            // 1 consumer
            s.spawn(|| {
                for _ in rx {
                    count += 1;
                }
            });
    
            // 10 producers
            let mut producer_handles = vec![];
            for _ in 0..10 {
                let tx = tx.clone();
                let handle = s.spawn(move || {
                    for _ in 0..100 {
                        tx.send(()).unwrap();
                    }
                });
                producer_handles.push(handle);
            }
            for handle in producer_handles {
                handle.join().unwrap();
            }
            drop(tx);
        });
    
        count
    }
    • use std::{thread, sync::atomic::{AtomicU32, Ordering::Relaxed}};
    • use std::{sync::mpsc, thread};
    • fn count() -> u32 {
    • let count = AtomicU32::new(0);
    • let (tx, rx) = mpsc::channel();
    • let mut count = 0_u32;
    • thread::scope(|s| {
    • // 1 consumer
    • s.spawn(|| {
    • for _ in rx {
    • count += 1;
    • }
    • });
    • // 10 producers
    • let mut producer_handles = vec![];
    • for _ in 0..10 {
    • s.spawn(|| {
    • let tx = tx.clone();
    • let handle = s.spawn(move || {
    • for _ in 0..100 {
    • let current = count.load(Relaxed);
    • count.store(current + 1, Relaxed);
    • tx.send(()).unwrap();
    • }
    • });
    • producer_handles.push(handle);
    • }
    • for handle in producer_handles {
    • handle.join().unwrap();
    • }
    • drop(tx);
    • });
    • count.into_inner()
    • count
    • }

The original code is great, I just try Iter for fun.

Code
Diff
  • fn split(string: &str, separator: char) -> Vec<&str> {
        string
            .chars()
            .fold(
                (0_usize, 0_usize, Vec::new()),
                |(mut l, mut r, mut splited), c| {
                    if c == separator {
                        splited.push(&string[l..r]);
                        l = r + 1;
                    }
                    if r == string.len() - 1 {
                        splited.push(&string[l..]);
                    }
                    r += 1;
                    (l, r, splited)
                },
            )
            .2
    }
    • fn split(string: &str, separator: char) -> Vec<&str> {
    • let mut split = Vec::new();
    • let mut i = 0;
    • for (j, c) in string.char_indices() {
    • if c == separator {
    • let slice = &string[i..j];
    • split.push(slice);
    • i = j + 1;
    • }
    • }
    • split.push(&string[i..]);
    • return split;
    • }
    • string
    • .chars()
    • .fold(
    • (0_usize, 0_usize, Vec::new()),
    • |(mut l, mut r, mut splited), c| {
    • if c == separator {
    • splited.push(&string[l..r]);
    • l = r + 1;
    • }
    • if r == string.len() - 1 {
    • splited.push(&string[l..]);
    • }
    • r += 1;
    • (l, r, splited)
    • },
    • )
    • .2
    • }