问题标签 [seqlock]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
2 回答
965 浏览

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

我想使用 c++11 原子库编写一个 seqlock。我在stackoverflow上阅读了一些关于seqlock的问题,但没有人帮助我。我使用的算法很常见,到处都能找到。这是我的代码:

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

我认为A和D不正确。

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

(1)处受保护数据的读取值在(4)处写入,(2)处flags的读取值在(3)处不写入(它是我们上次解锁写锁时写入的。)。即为什么我认为有一个错误。

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

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

0 投票
2 回答
280 浏览

c++ - GCC 使用 `memory_order_seq_cst` 跨负载重新排序。这是允许的吗?

使用基本 seqlock的简化版本,gcc 在load(memory_order_seq_cst)使用-O3. 使用其他优化级别编译或使用 clang(甚至 on O3)编译时,不会观察到这种重新排序。这种重新排序似乎违反了应该建立的同步关系,我很想知道为什么 gcc 会重新排序这个特定的负载,以及标准是否允许这样做。

考虑以下load函数:

在 seqlock 过程之后,此读取器旋转直到它能够加载 的两个实例seq_,这两个实例被定义为 a std::atomic<std::size_t>,它们是偶数(表示写入者当前未写入)和相等(表示写入者尚未写入value在两个负载之间seq_)。此外,由于这些负载被标记为memory_order_seq_cst( 作为默认参数 ),我想该指令copy = value;将在每次迭代时执行,因为它不能在初始负载中重新排序,也不能在后者之下重新排序。

但是,生成的程序集value在第一次加载之前发出加载,seq_甚至在循环之外执行。这可能导致不正确的同步或valueseqlock 算法无法解决的读取中断。此外,我注意到这只发生在 sizeof(value)123 字节以下时。修改value为某种类型 >= 123 字节会产生正确的程序集,并在每次循环迭代时在两次加载seq_. 为什么这个看似任意的阈值决定了生成哪个程序集?

该测试工具 暴露了我的 Xeon E3-1505M 上的行为,其中将从阅读器打印“Observed: 2”并返回值 65535。观察到的值seq_和返回的负载的组合value似乎违反了应该由编写线程发布seq.store(2)memory_order_release读取线程和读取线程建立的同步seq_关系memory_order_seq_cst

gcc 对负载重新排序是否有效,如果是,为什么它只在sizeof(value)< 123 时才这样做?clang,无论优化级别还是sizeof(value)不会重新排序负载。我相信 Clang 的 codegen 是适当且正确的方法。

0 投票
1 回答
1235 浏览

c++ - 用 32 位原子实现 64 位原子计数器

我想从原子 uint32 拼凑一个 uint64 原子计数器。计数器有一个写入器和多个读取器。writer 是一个信号处理程序,因此它不能阻塞。

我的想法是使用低位的生成计数作为读锁。读取器重试,直到整个读取过程中生成计数稳定,并且低位未设置。

以下代码在设计和使用内存排序方面是否正确?有没有更好的办法?

编辑:哎呀,已修复auto acquire = memory_order_release;

0 投票
1 回答
381 浏览

c - 为什么 rwlock 在 linux 内核中比 seqlock 更受欢迎?

读了 Robert Love 的 LKD 之后,我学习了 rwlock 和 seqlock,它们都是基于 spinlock 的。

在区分读写器时,rwlock 比 spinlock 更好,它会获得更好的性能。然而, rwlock 会让作家饿了。

seqlock 解决了 rwlock 使 writer 饿死的问题,但是 seqlock 的使用比 rwlock 少。那么,为什么 rwlock 比 seqlock 更受欢迎呢?

0 投票
1 回答
699 浏览

c - 哪些 seqlock 的实现是正确的?

我正在研究Seqlock的实现。然而,我发现的所有来源都以不同的方式实现它们。

Linux内核

Linux内核是这样实现的

基本上,它使用易失性读取加上读取屏障,在读取器端具有获取语义。

使用时,后续读取不受保护:

rigtorp/Seqlock

数据的加载仍然在没有原子操作或保护的情况下执行。但是,与内核中atomic_signal_fence的获取语义相反,在读取之前添加了获取释放语义。rmb

Amanieu/seqlock (锈)

seq加载和之间没有内存屏障data,而是在这里使用易失性读取。

Seqlocks 可以与编程语言记忆模型相处吗?(变体 3)

与 Rust 实现类似,但volatile_read在数据上使用原子操作而不是。

P1478R1中的参数:逐字节原子内存

该论文声称:

在一般情况下,有充分的语义理由要求此类 seqlock “临界区”内的所有数据访问必须是原子的。如果我们读取指针 p 作为读取数据的一部分,然后也读取 *p,如果读取 p 碰巧看到一半更新的指针值,则临界区中的代码可能会从错误地址读取。在这种情况下,可能无法避免使用传统的原子负载读取指针,而这正是我们所需要的。

然而,在许多情况下,特别是在多进程情况下,seqlock 数据由一个简单的可复制对象组成,而 seqlock “临界区”由一个简单的复制操作组成。在正常情况下,这可能是使用 memcpy 编写的。但这在这里是不可接受的,因为 memcpy 不会生成原子访问,并且(无论如何根据我们的规范)容易受到数据竞争的影响。

目前要正确编写这样的代码,我们基本上需要将这样的数据分解成许多小的无锁原子子对象,并一次复制一份。将数据视为单个大型原子对象会破坏 seqlock 的目的,因为原子复制操作将获取常规锁。我们的提议本质上添加了一个方便的库工具来自动分解成小对象。

我的问题

  1. 以上哪些实现是正确的?哪些是正确但低效的?
  2. 可以在volatile_readseqlock的获取读取之前重新排序吗?
0 投票
1 回答
89 浏览

c - 为什么需要 seqlock 的 IRQ 安全版本来进行读取访问?

当写访问受 seqlock 保护的共享资源时,写入者必须在进入临界区之前获得排他锁。因此,与自旋锁一样,使用 seqlock 进行写访问具有常见的变体(如 *_irqsave 和 *_bh)是有意义的。但是 LDD3(第 128 页)说:

如果您的 seqlock 可以从中断处理程序中访问,您应该使用 IRQ 安全版本来代替:

据我了解,由于它是为读者设计的,可以自由访问共享资源(仅在最后检查一致性并在需要时重试),因此读取访问被调度程序或硬件中断中断是完全可以的。我错过了什么吗?谢谢。

0 投票
1 回答
68 浏览

c++ - 可以/应该使用 SeqLock 实现非无锁原子吗?

在 MSVC STL 和 LLVMstd::atomic中,非原子大小的 libc++ 实现都是使用自旋锁实现的。

libc++ ( Github ):

MSVC(Github)(最近在这个问答中讨论过):

在考虑更好的实现时,我想知道用SeqLock替换它是否可行?如果读取不与写入竞争,优势将是廉价的读取。

我质疑的另一件事是是否可以改进 SeqLock 以使用 OS 等待。在我看来,如果读者观察到奇数,它可以使用原子等待底层机制(Linux futex/Windows WaitOnAddress)等待,从而避免自旋锁的饥饿问题。


在我看来,这似乎是可能的。尽管 C++ 内存模型目前不包括 Seqlock,但类型输入std::atomic必须是可简单复制的,因此memcpy如果使用足够的障碍来获得 volatile 等效项而不会严重破坏优化,则 seqlock 中的读/写将起作用并且将处理竞争。这将是特定 C++ 实现的头文件的一部分,因此它不必是可移植的。

关于在 C++ 中实现 SeqLock 的现有 SO Q&As(可能使用其他 std::atomic 操作)