-
hyc
"volatile int" guarantees atomic reads, but no promises about writes. stores are atomic, but increment/decrement are not.
-
hyc
read-modify-write ...
-
moneromooo
Since it was used for I/O, are you sure about writes ? Some ports or port ranges want to have ordered writes.
-
moneromooo
I don't remember a specific case though. Direct port twiddling seems so long ago...
-
UkoeHB
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' (
en.cppreference.com/w/cpp/atomic/memory_order ). Take these two cases for example:
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
-
UkoeHB
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)?
-
tusko
Whatever process grabs the mutex first gets it iirc
-
tusko
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.
-
UkoeHB
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)
-
UkoeHB
this is because memory order only has strong implications for operation ordering within a thread, not between threads
-
sech1
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
-
moneromooo
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.
-
sech1
no, in example 1 store is finished even before the thread is created
-
UkoeHB
the thread can already exist, e.g. if you have a thread pool
-
UkoeHB
(as an alternate example)
-
sech1
compiler can't reorder contructor and any other operator that uses contructed the object
-
sech1
*the constructed object
-
sech1
-
UkoeHB
how about this as a different example (no constructor or destructor involved)
paste.debian.net/hidden/5cc272f8
-
UkoeHB
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
-
UkoeHB
context, that means an inter-thread happens-before relationship. It's not clear to me where that relationship is established in these examples.
-
moneromooo
Oh nvm, I thought f was storing, and possibly racing with join.
-
sech1
in your examples, you start the second thread after Foo's constructor, so it is guaranteed there because it's single threaded
-
sech1
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
-
hyc
mutexes are synchronization points, nothing can be re-ordered around them
-
UkoeHB
hmm ok I am more convinced now
-
UkoeHB
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
-
UkoeHB
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:
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