0

In the Java memory model (JMM) in the Java Language Specification, if thread A writes a field and then unlocks a mutex, the write "happens before" the unlock. That means that two threads sharing a variable only while using the same synchronization block or mutex do not need that variable to be volatile for their changes to be visible, because the shared mutex will create a memory barrier.

Is the same thing true for C++?

Consider this code

std::mutex m;
int x = 0;
std::cout << "Starting async:\n";
auto future1 = async(launch::async, 
 [&] {  for (int i = 0; i < 100000; ++i) { m.lock();  ++x; m.unlock(); } });
auto future2 = async(launch::async, 
 [&] {  for (int i = 0; i < 100000; ++i) { m.lock();  ++x; m.unlock(); } });
std::cout << "After calling async. Waiting for result from async:\n";
future1.get(); // wait for thread1
future2.get(); // wait for thread1
std::cout << "x = " << x;

does X still need to be declared volatile in order for one thread to see the most recent change created by the other thread?

Gonen I
  • 5,576
  • 1
  • 29
  • 60
  • volatile does not do what you think it does, see dup. If you want to have guarantees about the state of `x` with respect to mutex locks and unlocks you should use an `std::atomic` and use operations with the desired memory ordering guarantees. – Botje Mar 09 '23 at 12:36
  • I'm not sure what you think I think. I certainly don't think that volatile replaces locking which is what I think you think that I think. – Gonen I Mar 09 '23 at 12:40
  • from "while using the same synchronization block or mutex do not need that variable to be volatile for their changes to be visible", I deduce that you think declaring a variable `volatile` makes sure that different threads see the same value after every modification, bypassing any caches that may hide modifications from another CPU core/thread. – Botje Mar 09 '23 at 12:41
  • 2
    If using a mutex then you don't need any other synchronisation, if not using a mutex you should use `std::atomic`. In neither case is `volatile` needed – Alan Birtles Mar 09 '23 at 12:43
  • perhaps the counter example is confusing, and I should have used a different example such as exit_now. If the two threads only access the exit_now shared variable while holding the same mutex, does it need to be volatile? – Gonen I Mar 09 '23 at 12:54
  • I'd like to add that this new policy in stackoverflow of immediately marking questions as duplicates and worse, closing them immediately is the exact opposite of what makes stackoverflow useful. Pointing someone to a huge question with multiple answers several pages long and saying "your answer is in there" is counter productive for everyone. Might as well send them a link to cplusplus reference's home page. – Gonen I Mar 09 '23 at 13:18
  • Marking questions as duplicate is the only way to keep the site sane. If you think the dup is irrelevant you can flag it for reopening. – Botje Mar 09 '23 at 13:57
  • You could, for example, remove all references to `volatile` and then the question becomes "does std::mutex introduce a memory barrier". I think [this answer](https://stackoverflow.com/a/12878500/1548468) sums up your question, as well as explaining (again) why volatile does not do what you think it does. – Botje Mar 09 '23 at 14:13
  • That actually does help. It only highlights the problem that many users are now left to search for their answer in the comments which they may have been lucky enough to receive on closed questions. The price of closing questions quickly outweighs the benefit. – Gonen I Mar 09 '23 at 14:29
  • @Botje The JMM doesn't use memory barriers are building blocks, and Java programs don't need "memory barriers". Just like C/C++. – curiousguy Mar 14 '23 at 21:58
  • @GonenI I think you are confusing Java/C# `volatile`, which is a variable for MT communication, and C/C++ `volatile`, which is a variable for system communication (like `printf` or `write`): in C and C++, `volatile` scalar variables work in conjunction with `ptrace`. – curiousguy Mar 14 '23 at 22:01
  • @curiousguy can you explain your last statement? I don't understand the relationship between ptrace and volatile nor what you mean by "system communication". – Botje Mar 14 '23 at 22:23
  • @Botje In C `volatile` was used historically to communicate with devices, via special memory addresses, where even reading a specific memory address could produce a SE (side effect); such memory shouldn't be cached but at the time, there was no CPU cache anyway... Later `volatile` was used for more cases that didn't involve direct I/O with devices; `volatile` in itself doesn't imply "not cached", you have to manage that yourself. Marking a scalar `volatile` implies that all operations on the variable really happen in "memory" that is in the CPU view of memory. – curiousguy Mar 15 '23 at 20:21
  • Making sure each operation on a scalar happens in the memory viewed by the CPU makes it possible for other processes to use ptrace (or any equivalent) to view or change the variable. So the real serious specification of `volatile` happens to be "what you can change with ptrace", except std writers aren't men enough to actually write that, so I'm the only person writing that (also, I'm the only person being serious about C and C++). – curiousguy Mar 15 '23 at 20:23

0 Answers0