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> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spinlock_i32() {
// mark `Sync` explicitly for i32
let spinlock = &SpinLock::new(0);
std::thread::scope(|s| {
for _ in 0..50 {
s.spawn(move || {
let value = spinlock.lock();
*value += 1;
spinlock.unlock();
});
}
});
assert_eq!(50, *spinlock.lock());
}
}
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()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple() {
let mut vh = VecHolder::new();
vh.push(1);
vh.push(2);
vh.push(3);
assert_eq!(&vh.vec_, &[1, 2, 3]);
}
#[test]
fn test_random() {
let mut vh = VecHolder::new();
for i in 0..100 {
vh.push(i);
}
assert_eq!(vh.vec_.len(), 100);
let mut num = 99;
while let Some(i) = vh.pop() {
assert_eq!(i, num);
num -= 1;
}
}
}
Use std::sync::mpsc to achieve the target.
Ignore the bad performance, just for fun~
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.
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
- }