无锁队列中获取系统时间时使用atomic_signal_fence的原因?
QueryPerformanceCounter in Lock-Free Queues Great question—let’s break this down clearly, since your observation that this pattern isn’t common is spot-on, and there’s a specific context driving these barriers.
First, let’s clarify what CompilerMemBar() does here:
#define CompilerMemBar() std::atomic_signal_fence(std::memory_order_seq_cst)
This is a compiler-only memory barrier. It tells the compiler not to reorder any instructions across this fence, but it has no impact on CPU-level instruction reordering. Unlike heavier thread fences, it’s a lightweight tool often repurposed to enforce strict instruction ordering at the compiler level, even though its primary design is for thread-signal handler interactions.
Why wrap QueryPerformanceCounter with these barriers?
The key context here is that this code lives in a lock-free queue—a scenario where instruction ordering can break correctness, even if it doesn’t seem directly tied to time precision. Here are the two core rationales:
Prevent compiler reordering of time-sensitive queue logic
Lock-free queues often use timestamps for critical logic: timeout checks, ordering pending operations, or tracking how long items sit in the queue.Normally, compilers treat WINAPI calls like
QueryPerformanceCounteras "opaque" (assuming they have side effects) and won’t reorder user code around them. But at high optimization levels (like/O2in MSVC), aggressive compilers might reorder instructions if they don’t detect an explicit dependency.For example, imagine queue code like this:
SystemTime start = getSystemTime(); bool enqueued = try_enqueue(item); if (!enqueued && (getSystemTime() - start) > TIMEOUT) { /* handle timeout */ }Without the barriers, the compiler could reorder the
getSystemTime()call to run aftertry_enqueue(), making the timeout check completely invalid. The barriers force the compiler to executeQueryPerformanceCounterexactly where it’s written, ensuring the timestamp is tied to the correct point in the queue’s logic.Ensure consistency with adjacent atomic operations
Lock-free queues rely heavily on atomic operations for head/tail pointer manipulations. Whileatomic_signal_fencedoesn’t enforce CPU-level ordering, it guarantees the compiler won’t reorder theQueryPerformanceCountercall relative to these atomic operations.For instance, if the queue uses timestamps to resolve thread contention, you need to guarantee the timestamp is captured either before or after an atomic load/store—not reordered across it. The barriers enforce this strict ordering at the compiler level.
Do these barriers affect time precision?
No—they don’t change the accuracy or precision of the value returned by QueryPerformanceCounter. That function always returns the current high-resolution counter value at the moment it executes. The problem isn’t with the value itself, but with when that value is captured relative to other queue operations.
Why isn’t this pattern common?
Most code doesn’t operate under the strict ordering constraints of lock-free data structures. For regular applications, the compiler’s default treatment of WINAPI calls as opaque is enough to prevent problematic reordering. Additionally, std::atomic_signal_fence is a niche tool—many developers would use MSVC-specific _ReadWriteBarrier() or heavier std::atomic_thread_fence if they needed a compiler barrier, making this pattern less ubiquitous.
In short, these barriers are a defensive measure tailored to lock-free code’s unique demands, where even tiny reorderings can cause subtle, hard-to-debug correctness issues—not a general fix for time precision.
内容的提问来源于stack exchange,提问作者calvin

