43 static_assert(std::is_trivially_copyable_v<T>,
44 "SeqLock<T> requires T to be trivially copyable");
49 store_payload(initial);
54 store_payload(initial);
63 seq_.fetch_add(1, std::memory_order_acq_rel);
65 seq_.fetch_add(1, std::memory_order_release);
74 unsigned seq0 = seq_.load(std::memory_order_acquire);
75 if (seq0 & 1)
continue;
77 std::atomic_thread_fence(std::memory_order_acquire);
78 unsigned seq1 = seq_.load(std::memory_order_acquire);
79 if (seq0 == seq1)
return result;
84 using PayloadWord = std::uintptr_t;
85 static constexpr std::size_t kWordSize =
sizeof(PayloadWord);
86 static constexpr std::size_t kWordCount =
sizeof(T) / kWordSize;
87 static constexpr std::size_t kTailBytes =
sizeof(T) % kWordSize;
89 static_assert(std::atomic<PayloadWord>::is_always_lock_free,
90 "SeqLock payload word atomics must be lock-free");
91 static_assert(std::atomic<std::uint8_t>::is_always_lock_free,
92 "SeqLock payload byte atomics must be lock-free");
94 void store_payload(
const T& value)
noexcept {
95 const auto* bytes =
reinterpret_cast<const std::uint8_t*
>(&value);
96 for (std::size_t i = 0; i < kWordCount; ++i) {
98 std::memcpy(&word, bytes + i * kWordSize, kWordSize);
99 payload_words_[i].store(word, std::memory_order_relaxed);
101 for (std::size_t i = 0; i < kTailBytes; ++i) {
102 payload_tail_[i].store(bytes[kWordCount * kWordSize + i],
103 std::memory_order_relaxed);
107 void load_payload(T& result)
const noexcept {
108 auto* bytes =
reinterpret_cast<std::uint8_t*
>(&result);
109 for (std::size_t i = 0;
i < kWordCount; ++
i) {
110 const PayloadWord word =
111 payload_words_[
i].load(std::memory_order_relaxed);
112 std::memcpy(bytes + i * kWordSize, &word, kWordSize);
114 for (std::size_t i = 0;
i < kTailBytes; ++
i) {
115 bytes[kWordCount * kWordSize +
i] =
116 payload_tail_[
i].load(std::memory_order_relaxed);
120 alignas(64) std::array<std::atomic<PayloadWord>, kWordCount> payload_words_{};
121 std::array<std::atomic<std::uint8_t>, kTailBytes> payload_tail_{};
122 std::atomic<unsigned> seq_{0};