boost vs std atomic sequential consistency semantics
I've asked the question in stack overflow, but no luck there. I hope I will see some light here. I'd like to write a C++ object where there are many logger threads logging to a large global (non-atomic) ring buffer, with an occasional reader thread which wants to read as much data in the buffer as possible. I ended up having a global atomic counter where loggers get locations to write to, and each logger increments the counter atomically before writing. The reader tries to read the buffer and per-logger local (atomic) variable to know whether particular buffer entries are busy being written by some logger, so as to avoid using them. So I have to do synchronization between a pure reader thread and many writer threads. I sense that the problem can be solved without using locks, and I can rely on "happens after" relation to determine whether my program is correct. I've tried relaxed atomic operation, but it won't work: atomic variable stores are releases and loads are acquires, and the guarantee is that some acquire (and its subsequent work) always "happens after" some release (and its preceding work). That means there is no way for the reader thread (doing no store at all) to guarantee that something "happens after" the time it reads the buffer, which means I don't know whether some logger has overwritten part of the buffer when the thread is reading it. So I turned to sequential consistency. For me, "atomic" means Boost.Atomic, which notion of sequential consistency has a "pattern" documented http://www.boost.org/doc/libs/1_57_0/doc/html/atomic/thread_coordination.htm... : The third pattern for coordinating threads via Boost.Atomic uses seq_cst for coordination: If ... 1. thread1 performs an operation A, 2. thread1 subsequently performs any operation with seq_cst, 3. thread1 subsequently performs an operation B, 4. thread2 performs an operation C, 5. thread2 subsequently performs any operation with seq_cst, 6. thread2 subsequently performs an operation D, then either "A happens-before D" or "C happens-before B" holds. Note that the second and fifth lines say "any operation", without saying whether it modify anything, or what it operates on. This provides the guarantee that I wanted. All is happy until I watch the talk of Herb Sutter titled "atomic<> Weapons". What he implied is that seq_cst is just a acq_rel, with the additional guarantee of consistent atomic stores ordering. The suggestion that sequentially consistent atomic operations may be re-ordered with non-atomic operations only adds to my mystery. I turned to the cppreference.com http://en.cppreference.com/w/cpp/atomic/memory_order, which have similar descriptions. So my questions: 1. Does C++11 and Boost Atomic implement the same memory model? 2. If (1) is "yes", does it mean the "pattern" described by Boost is somehow implied by the C++11 memory model? How? Or does it mean the documentation of either Boost or C++11 in cppreference is wrong? 3. If (1) is "yes", but (2) is "Boost documentation is incorrect", is there any way to achieve the effect I want, namely to have guarantee that (the work subsequent to) some atomic store happens after (the work preceding) some atomic load? 4. If (1) is "no", is there any way to achieve the effect I want in C++11?
Have you tried Boost LockFree ? http://www.boost.org/doc/libs/1_55_0/doc/html/lockfree.html which handle multiple producer/Multiple-consumer queue Le 15/04/2015 08:42, Isaac To a écrit :
I've asked the question in stack overflow, but no luck there. I hope I will see some light here.
I'd like to write a C++ object where there are many logger threads logging to a large global (non-atomic) ring buffer, with an occasional reader thread which wants to read as much data in the buffer as possible. I ended up having a global atomic counter where loggers get locations to write to, and each logger increments the counter atomically before writing. The reader tries to read the buffer and per-logger local (atomic) variable to know whether particular buffer entries are busy being written by some logger, so as to avoid using them.
So I have to do synchronization between a pure reader thread and many writer threads. I sense that the problem can be solved without using locks, and I can rely on "happens after" relation to determine whether my program is correct.
I've tried relaxed atomic operation, but it won't work: atomic variable stores are releases and loads are acquires, and the guarantee is that some acquire (and its subsequent work) always "happens after" some release (and its preceding work). That means there is no way for the reader thread (doing no store at all) to guarantee that something "happens after" the time it reads the buffer, which means I don't know whether some logger has overwritten part of the buffer when the thread is reading it.
So I turned to sequential consistency. For me, "atomic" means Boost.Atomic, which notion of sequential consistency has a "pattern" documented http://www.boost.org/doc/libs/1_57_0/doc/html/atomic/thread_coordination.htm...:
The third pattern for coordinating threads via Boost.Atomic uses seq_cst for coordination: If ...
1. thread1 performs an operation A, 2. thread1 subsequently performs any operation with seq_cst, 3. thread1 subsequently performs an operation B, 4. thread2 performs an operation C, 5. thread2 subsequently performs any operation with seq_cst, 6. thread2 subsequently performs an operation D,
then either "A happens-before D" or "C happens-before B" holds.
Note that the second and fifth lines say "any operation", without saying whether it modify anything, or what it operates on. This provides the guarantee that I wanted.
All is happy until I watch the talk of Herb Sutter titled "atomic<> Weapons". What he implied is that seq_cst is just a acq_rel, with the additional guarantee of consistent atomic stores ordering. The suggestion that sequentially consistent atomic operations may be re-ordered with non-atomic operations only adds to my mystery. I turned to the cppreference.com http://en.cppreference.com/w/cpp/atomic/memory_order, which have similar descriptions.
So my questions:
1. Does C++11 and Boost Atomic implement the same memory model? 2. If (1) is "yes", does it mean the "pattern" described by Boost is somehow implied by the C++11 memory model? How? Or does it mean the documentation of either Boost or C++11 in cppreference is wrong? 3. If (1) is "yes", but (2) is "Boost documentation is incorrect", is there any way to achieve the effect I want, namely to have guarantee that (the work subsequent to) some atomic store happens after (the work preceding) some atomic load? 4. If (1) is "no", is there any way to achieve the effect I want in C++11?
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Olivier Tristan Research & Development www.uvi.net
participants (2)
-
Isaac To
-
Olivier Tristan