I am using a seq_lock for coordination between a reader and a writer thread. The reader calls load(), and the writer calls store(). Here’s a simplified version of my code:
#include <atomic>
#include <string.h>
std::atomic<std::size_t> seq_;
std::size_t push_seq = 0;
char value_[32];
char load_copy[32];
char store_copy[32];
void load() {
std::size_t seq0, seq1;
do {
seq0 = seq_.load(std::memory_order_acquire);
memcpy(load_copy, value_, 32);
seq1 = seq_.load(std::memory_order_relaxed);
} while (seq0 != seq1 || seq0 & 1);
}
void store() {
seq_.store(++push_seq, std::memory_order_relaxed);
memcpy(value_, store_copy, 32);
seq_.store(++push_seq, std::memory_order_release);
}
Problem:
In the store() function, it’s possible that memcpy can be executed before seq_.store(std::memory_order_release), violating the intended memory ordering.
Similarly, in the load() function, memcpy might be executed before seq_.load(std::memory_order_acquire) completes, causing inconsistent data to be read.
My Attempt:
I am considering using std::atomic_thread_fence with appropriate memory orderings (memory_order_acquire and memory_order_release) to prevent this reordering.
Will adding std::atomic_thread_fence in this case solve the problem, or should I be using a different approach to prevent memcpy from being reordered relative to the seq_ operations? Any guidance on how to ensure proper memory consistency here would be helpful.
Will this work?
void load() {
std::size_t seq0, seq1;
do {
seq0 = seq_.load(std::memory_order_acquire);
memcpy(load_copy, value_, 32);
std::atomic_thread_fence(std::memory_order_release);
seq1 = seq_.load(std::memory_order_relaxed);
} while (seq0 != seq1 || seq0 & 1);
}
void store() {
seq_.store(++push_seq, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
memcpy(value_, store_copy, 32);
seq_.store(++push_seq, std::memory_order_release);
}
Note that there is only one reader and one writer
12