Futex设计和退避

我正在写一些没有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内存顺序,这些位置还可以吗?

  • 我使用指数退避方法,这是正确的选择吗?

iCMS 回答:Futex设计和退避

暂时没有好的解决方案,如果你有好的解决方案,请发邮件至:iooj@foxmail.com
本文链接:https://www.f2er.com/1511330.html

大家都在问