如何使用c ++ 11原子库实现seqlock锁

时间:2013-12-03 04:06:32

标签: c++ c++11 atomic

我想用c ++ 11原子库编写一个seqlock。我已经在stackoverflow上读了一些关于seqlock的问题,但是没有人帮助过我。我使用的算法很常见,你可以在任何地方找到它。那是我的代码:

struct sequence_spinlock_t {

    void write_lock() {
        lock.lock();
        flags.fetch_add(1, memory_order_acquire); //A

    }

    void write_unlock() {
        flags.fetch_add(1, memory_order_release); //B
        lock.unlock();
    }

    void read_enter(uintptr_t *flag) {
        for (;;) {
            uintptr_t f = flags.load(memory_order_acquire); //C
            if ((f & 1) == 0) {
                *flag = f;
                break;
            }
            pause();
        }
    }

    bool_ read_leave(uintptr_t flag) {                                        

        uintptr_t f = flags.load(memory_order_relaxed); //D
        return f == flag;
    }

    spinlock_t lock;
    atomic_uintptr_t flags;
};

    //read thread
    uintptr_t flag;
    do {
        lock.read_enter(&flag);      (0)
        //read something             (1)
    } while(!lock.read_leave(flag))  (2)


    //write thread
    lock.write_lock();              (3)
    //write something               (4)
    lock.write_unlock();            (5)

我确保在B和C处正确使用memory_order标签。

我不认为A和D是正确的。

考虑一下我们同时读取和写入受保护的数据。我担心D处标志的读取值太旧,我们没有读取write_lock()写入的最新值。但是我们读取了最新值写线程写的受保护数据(可能不会发生在x86系统上,但我不认为代码是在x86上运行的。)。读取线程完成后读取受保护的数据,因为标志的读取值太旧了,我没有发现序列已经增加。线程从循环中产生并且我们制造了一个错误。

(1)处的受保护数据的读取值写入(4),(2)处的标志读取值不写入(3)(当我们上次解锁写锁定时写入)。那就是为什么我认为有一个错误。

但我真的不知道要解决这个问题。我试图在read_leavee()和write_locke()之间建立一个“同步”关系(我希望“read_leave()与write_locke()同步”)。但是那里在read_leave()中没有存储操作,所以我失败了。

(哦!c ++标准规范对我来说太难理解。部分原因是因为我不是来自英语国家。)

2 个答案:

答案 0 :(得分:1)

在read_leave中使用memory_order_relaxed本身就可以了,但确实需要确保在加载flag变量之前已经加载了数据值。您可以使用std :: atomic_thread_fence执行此操作。即你的read_leave应该看起来像

bool read_leave(uintptr_t flag) { atomic_thread_fence(memory_order_acquire); uintptr_t f = flag.load(memory_order_relaxed); return f == flag; }

FWIW,通过此更改,您的代码与http://safari.ece.cmu.edu/MSPC2012/slides_posters/boehm-slides.pdf

中的示例3大致相同

答案 1 :(得分:1)

值得注意的是,seqlock保护的数据必须具有原子类型,并且必须使用原子加载/存储来访问它。不理想,但这就是C11 / C ++ 11给你的。 Han Boehm的MSPC论文详细介绍了这一点。