02:31:32 "volatile int" guarantees atomic reads, but no promises about writes. stores are atomic, but increment/decrement are not. 02:32:02 read-modify-write ... 07:37:23 Since it was used for I/O, are you sure about writes ? Some ports or port ranges want to have ordered writes. 07:38:13 I don't remember a specific case though. Direct port twiddling seems so long ago... 17:30:02 sech1: After banging my head on memory_order for a while I think I more-or-less understand it. What I don't understand is how to guarantee 'inter-thread happens-before' ( https://en.cppreference.com/w/cpp/atomic/memory_order ). Take these two cases for example: https://paste.debian.net/hidden/ae3c37e0/ . Can those asserts theoretically fail according to the C++ standard, or am I missing something? Even if you wrap everything in 17:30:02 mutexes, is there a guarantee that a acquiring a mutex lock in constructor Foo() won't be delayed until after thread2 calls .f() (in example 1)? 17:31:13 Whatever process grabs the mutex first gets it iirc 17:31:54 its first-come first-serve like that, the other process will block on a mutex lock. If blocking is undesirable then a semaphore or condition variable could be used. 17:34:13 yes, but 'when do you decide to grab the mutex' appears to be indeterminate, so even if a mutex lock is sequenced before passing a lambda to another thread, it doesn't have to be executed until after that (as far as I can tell) 17:35:18 this is because memory order only has strong implications for operation ordering within a thread, not between threads 17:36:08 UkoeHB both examples won't trigger the assert because you create the second thread too late. In the first example, value is already = 1; in the second example, destructor will be called after thread2.join 17:36:18 In example 1, f side effects will be finished before join returns. Otherwise it'd make join useless, and they'd have noticed that before. 17:39:10 no, in example 1 store is finished even before the thread is created 17:39:32 the thread can already exist, e.g. if you have a thread pool 17:39:44 (as an alternate example) 17:39:47 compiler can't reorder contructor and any other operator that uses contructed the object 17:40:06 *the constructed object 17:41:45 anyway, this explains it better: https://en.cppreference.com/w/cpp/atomic/memory_order#Release-Acquire_ordering 17:43:08 how about this as a different example (no constructor or destructor involved) https://paste.debian.net/hidden/5cc272f8/ 17:46:00 sech1: yeah that page says "once the atomic load is completed, thread B is guaranteed to see everything thread A wrote to memory. This promise only holds if B actually returns the value that A stored, or a value from later in the release sequence. " note where it says 'only holds if B actually returns the value that A stored'. There is no guarantee that B will return that value, if A did not happen-before B. In a multithreaded 17:46:00 context, that means an inter-thread happens-before relationship. It's not clear to me where that relationship is established in these examples. 17:50:03 Oh nvm, I thought f was storing, and possibly racing with join. 17:59:01 in your examples, you start the second thread after Foo's constructor, so it is guaranteed there because it's single threaded 17:59:57 well, if you have a thread pool then you need to wait for the correct value in thread 2 then you have a guarantee that you'll see side effects from thread 1 18:17:05 mutexes are synchronization points, nothing can be re-ordered around them 18:25:50 hmm ok I am more convinced now 18:25:50 1. when spinning up a new thread, a mutex will be acquired, and mutexes have at least memory_order_acq_rel, so any statements before and after the mutex is acquired won't be reordered 18:25:50 2. when passing data to another thread safely (not via a reference), either a mutex will be acquired to lock some shared data structure, or an atomic with at least memory_order_acq_rel will be mutated (for example: https://paste.debian.net/hidden/8ef0e92c/ ); either way, any statements before passing the data cannot be reordered after the point where the data becomes available to another thread