On 2/24/2019 3:05 AM, Chris Vine wrote:
> On Sat, 23 Feb 2019 20:39:09 -0800
> "Chris M. Thomasson" <
invalid_chris_t...@invalid.com>
> wrote:
>> On 2/23/2019 8:25 PM, Chris M. Thomasson wrote:
>>> On 2/23/2019 4:23 PM, Chris Vine wrote:
>>>> On Sat, 23 Feb 2019 15:09:58 -0800 (PST)
>>>>
mvor...@gmail.com wrote:
>>>>> On Saturday, February 23, 2019 at 3:44:40 PM UTC-5,
mvor...@gmail.com
>>>>> wrote:
>>>>>> Is it possible for this simple program to print false? Given the
>>>>> compiler or CPU can reorder around the relaxed atomic. Or is my
>>>>> understanding off.
>> [...]
>>> :^D
>>>
>>> Using standalone fences is my habit of choice. You sort if intermingled
>>> it here but nice nonetheless:
>>>
>>>
https://groups.google.com/d/topic/lock-free/A1nzcMBGRzU/discussion
>>>
>>>
>>
>> Fwiw, programming on the SPARC basically got me on this. Wrt my comment
>> about intermingling, I accidentally read your release barrier as being
>> part of the atomic itself, not standalone. Sorry for any confusion
>> Chris. ;^o
>
> As I read the standard[1] I think this "intermingling" works:
Indeed it does. Afaict, a std::atomic<T> behaves as if it were a fence
wrt the memory order.
> std::atomic<bool> flag{false};
> bool data{false};
>
> std::thread t1([&]() {
> data = true;
> flag.store(true, std::memory_order_release);
> });
>
> std::thread t2([&]() {
> while(!flag.load(std::memory_order_relaxed));
> std::atomic_thread_fence(std::memory_order_acquire);
> cout << "data = " << boolalpha << data << endl;
> });
>
> t1.join();
> t2.join();
>
> return 0;
>
> However, like you I don't do that because it looks a bit odd to me.
Totally agreed. At least it is guaranteed to work in the std.
> [1] §29.8/4 of C++11 says "An atomic operation A that is a release
> operation on an atomic object M synchronizes with an acquire fence B if
> there exists some atomic operation X on M such that X is sequenced
> before B and reads the value written by A or a value written by any
> side effect in the release sequence headed by A."
>
Right. I read this as if std::atomic behaves as a
std::atomic_thread_fence. However, once we introduce anything other than
relaxed in a std::atomic, it can "hide" where the actual barrier is
executed, or placed in the code.
For instance, a std::atomic::exchange with acquire will execute the
barrier _after_ the atomic RMW of exchange occurs. Using the standalone
fence allows the user to place it exactly where it is needed. It is more
complex, but can be more efficient...
Using a std::atomic::exchange with release will execute the barrier
_before_ the atomic RMW of exchange occurs.
Well, sometimes the standalone can be more flexible, and at least a lot
more "informative" wrt showing exactly where the barriers actually need
to be.