我正在写一些没有libc
的东西,我需要一个互斥锁进行同步(我使用了#![cfg_attr(not(test),no_std)]
)。这是我的代码:
const FREE: u8 = 0;
const LOCKED: u8 = 1;
const FUTEX_MODE: u8 = 2;
const BACKOFF_LIMIT: usize = 6;
#[inline(always)]
pub fn futex_wait(target: &AtomicU8,target_value: u8) {
unsafe {
match syscall!(SYS_futex,target as *const AtomicU8,FUTEX_WAIT_PRIVATE,target_value,0) {
_ => ()
}
}
}
#[inline(always)]
pub fn futex_wake_one(target: &AtomicU8) {
unsafe {
syscall!(SYS_futex,FUTEX_WAKE_PRIVATE,1,0).unwrap();
}
}
#[derive(Default)]
pub struct Backoff {
counter: usize,}
impl Backoff {
#[inline(always)]
pub fn wait(&mut self) -> bool {
unsafe {
if self.counter < BACKOFF_LIMIT {
for _ in 0..(1 << self.counter) {
spin_loop_hint();
}
self.counter += 1;
true
} else {
syscall!(SYS_sched_yield).unwrap();
false
}
}
}
}
pub struct Futex<T> {
_flag: AtomicU8,item: core::cell::UnsafeCell<T>,}
pub struct FutexHandle<'a,T> {
_futex: &'a Futex<T>,item: &'a mut T,}
unsafe impl<T> Sync for Futex<T> {}
unsafe impl<T> Send for Futex<T> {}
impl<T> Futex<T> {
pub fn new(item: T) -> Self {
Futex {
_flag: AtomicU8::new(FREE),item: UnsafeCell::new(item),}
}
#[inline(always)]
fn raw_lock(&self) {
let mut backoff = Backoff::default();
loop {
if self._flag.compare_and_swap(FREE,LOCKED,Ordering::Relaxed) == FREE {
return;
}
if !backoff.wait() {
break;
}
}
loop {
if self._flag.load(Ordering::Relaxed) == FUTEX_MODE
|| self._flag.compare_and_swap(LOCKED,FUTEX_MODE,Ordering::SeqCst) == LOCKED {
futex_wait(&self._flag,FUTEX_MODE);
}
if self._flag.compare_and_swap(FREE,Ordering::SeqCst) == FREE {
break;
}
}
}
#[inline(always)]
fn raw_unlock(&self) {
if self._flag.fetch_sub(1,Ordering::SeqCst) == FUTEX_MODE {
self._flag.store(FREE,Ordering::Relaxed);
futex_wake_one(&self._flag);
}
}
pub fn lock(&self) -> FutexHandle<T> {
self.raw_lock();
unsafe {
FutexHandle {
_futex: &self,item: &mut *self.item.get(),}
}
}
}
impl<'a,T> Drop for FutexHandle<'a,T> {
fn drop(&mut self) {
self._futex.raw_unlock();
}
}
impl<'a,T> Deref for FutexHandle<'a,T> {
type Target = T;
fn deref(&self) -> &Self::Target {
return self.item;
}
}
impl<'a,T> DerefMut for FutexHandle<'a,T> {
fn deref_mut(&mut self) -> &mut Self::Target {
return self.item;
}
}
该代码已经通过了一些基本测试:
#[cfg(test)]
mod test {
use std::sync::Arc;
use super::*;
#[test]
fn test_futex() {
let data = Arc::new(Futex::new(0));
let mut handles = Vec::new();
for _ in 0..100 {
let data = data.clone();
handles.push(std::thread::spawn(move || {
let mut handle = data.lock();
*handle += 1;
std::thread::sleep(std::time::Duration::from_micros(50));
println!("done: {}",*handle);
}));
}
for i in handles {
i.join();
}
{
let handle = data.lock();
assert_eq!(*handle,100);
}
}
}
但是我有点担心自己是否做对了:
-
我使用了两个
Relaxed
内存顺序,这些位置还可以吗? -
我使用指数退避方法,这是正确的选择吗?