Lok*_*esh -4 concurrency spinlock compare-and-swap rust
我试图实现我自己的自定义 SpinLock,但 SpinLock 似乎行为不当。
\n我有两个文件,main.rs并且safe.rs.
测试在 Ubuntu 22.04.3LTS 中进行,系统规格为 4GB RAM、64 位处理器、AMD\xc2\xae Pro a4-3350b APU 和 Radeon r4 显卡。
\n这是错误消息:
\nloki@loki:~/main/vs/actic/rust-nomic/spin-lock$ cargo run RUST_BACKTRACE=1\n Compiling spin-lock v0.1.0 (/home/loki/main/vs/actic/rust-nomic/spin-lock)\n Finished dev [unoptimized + debuginfo] target(s) in 0.98s\n Running `target/debug/spin-lock RUST_BACKTRACE=1`\nHello, world!\nthread 'main' panicked at 'assertion failed: `(left == right)`\n left: `9999995`,\n right: `10000000`', src/main.rs:15:5\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\nRun Code Online (Sandbox Code Playgroud)\nsafe.rs:
use core::ops::{Deref,DerefMut};\nuse core::sync::atomic::{AtomicBool,Ordering::{Acquire,Release}};\nuse core::cell::UnsafeCell;\nuse core::hint::spin_loop;\n\n#[derive(Debug)]\npub struct SpinLock<T>{\n // Status true -> its locked || Status false -> its un_locked(ready to lock)\n status:AtomicBool,\n pub data:UnsafeCell<T>\n}\n\npub struct SpinGuard<'a,T>{\n lock:&'a SpinLock<T>\n}\nunsafe impl<T> Sync for SpinLock<T> where T:Send{}\n\nimpl<T> SpinLock<T>{\n #[inline]\n pub const fn new(data:T)->Self{\n Self { status: AtomicBool::new(false), data: UnsafeCell::new(data) }\n }\n pub fn lock(&self)->SpinGuard<T>{\n while self.status.swap(true,Acquire){\n spin_loop();\n }\n SpinGuard { lock: self }\n }\n}\n\n\nimpl<'a,T> SpinGuard<'a,T>{\n pub fn release(self){\n self.lock.status.store(false, Release)\n }\n}\n\nimpl<T> Deref for SpinGuard<'_,T>{\n type Target = T;\n fn deref(&self) -> &Self::Target {\n unsafe{&*self.lock.data.get()}\n }\n}\n\nimpl<T> DerefMut for SpinGuard<'_,T>{\n fn deref_mut(&mut self) -> &mut Self::Target {\n unsafe{&mut *self.lock.data.get()}\n }\n}\n\nimpl<T> Drop for SpinGuard<'_,T>{\n fn drop(&mut self) {\n self.lock.status.store(false, Release)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\nmain.rs:
use std::thread;\nmod safe;\nfn main() {\n println!("Hello, world!");\n let mut x=safe::SpinLock::new(0);\n thread::scope(|t|{\n for _ in 0..10000000{\n t.spawn(||{\n let mut q=x.lock();\n *q+=1;\n q.release();\n });\n }\n });\n assert_eq!(x.data.get_mut(),&mut 10000000);\n println!("{:?}",x);\n}\nRun Code Online (Sandbox Code Playgroud)\n
通过添加几个sleeps,您可以使错误更加可重现:
use std::thread;
fn main() {
println!("Hello, world!");
let mut x = safe::SpinLock::new(0);
thread::scope(|t| {
for _ in 0..100 {
t.spawn(|| {
let mut q = x.lock();
let q_prev = *q;
std::thread::sleep(std::time::Duration::from_millis(1));
*q = q_prev + 1;
q.release();
});
}
});
assert_eq!(x.data.get_mut(), &mut 100);
println!("{:?}", x.data.get_mut());
}
mod safe {
use core::cell::UnsafeCell;
use core::hint::spin_loop;
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{
AtomicBool,
Ordering::{Acquire, Release},
};
#[derive(Debug)]
pub struct SpinLock<T> {
// Status true -> its locked || Status false -> its un_locked(ready to lock)
status: AtomicBool,
pub data: UnsafeCell<T>,
}
pub struct SpinGuard<'a, T> {
lock: &'a SpinLock<T>,
}
unsafe impl<T> Sync for SpinLock<T> where T: Send {}
impl<T> SpinLock<T> {
#[inline]
pub const fn new(data: T) -> Self {
Self {
status: AtomicBool::new(false),
data: UnsafeCell::new(data),
}
}
pub fn lock(&self) -> SpinGuard<T> {
while self.status.swap(true, Acquire) {
spin_loop();
}
SpinGuard { lock: self }
}
}
impl<'a, T> SpinGuard<'a, T> {
pub fn release(self) {
self.lock.status.store(false, Release);
std::thread::sleep(std::time::Duration::from_millis(1));
}
}
impl<T> Deref for SpinGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.lock.data.get() }
}
}
impl<T> DerefMut for SpinGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.lock.data.get() }
}
}
impl<T> Drop for SpinGuard<'_, T> {
fn drop(&mut self) {
self.lock.status.store(false, Release)
}
}
}
Run Code Online (Sandbox Code Playgroud)
Hello, world!
thread 'main' panicked at src\main.rs:17:5:
assertion `left == right` failed
left: 29
right: 100
Run Code Online (Sandbox Code Playgroud)
原因是你解锁了两次。有点隐蔽,不过.release()功能其实是直接解锁一次,在drop()功能里解锁一次。
只需删除函数中的第一个解锁即可.release(),然后就可以了:)
作为另一个挑剔,我会将Acquirein替换swap为AcqRel. 但我不能 100% 确定它会产生影响。
Hello, world!
thread 'main' panicked at src\main.rs:17:5:
assertion `left == right` failed
left: 29
right: 100
Run Code Online (Sandbox Code Playgroud)
Hello, world!
100
Run Code Online (Sandbox Code Playgroud)