In the code base that I reviewed, I found the following idiom.
void notify(struct actor_t act) { write(act.pipe, "M", 1); } // thread A sending data to thread B void send(byte *data) { global.data = data; notify(threadB); } // in thread B event loop read(this.sock, &cmd, 1); switch (cmd) { case 'M': use_data(global.data);break; ... }
“Hold it,” I said to the author, a senior member of my team, “there is no memory barrier! You cannot guarantee that global.data will be global.data from the cache to main memory. If thread A and thread B work on two different processors, this the circuit may fail. "
The senior programmer grinned and explained slowly, as if explaining to his five-year-old boy how to tie his shoelaces: “Listen, boy, we saw a lot of problems here related to flow, in high-load tests and in real clients,” he paused to scratching his long beard, "but we never had a mistake with this idiom."
"But it is said in the book ..."
“Quiet!”, He quickly shut up: “Theoretically, this is not guaranteed, but in practice the fact that you used a function call is actually a memory barrier. The compiler will not change the order of the global.data = data instructions, since it cannot know if anyone will use it in the function call, and the x86 architecture will ensure that other processors see this piece of global data by the time that thread B reads the command from the channel. Be sure that we have enough real world problems about which need to worry. We do not need to invest extra effort in fictitious theoretical problems.
"Rest assured, my boy, in time you will understand to separate the real problem from the problems associated with I-need-to-get-a-PhD."
Is he right? Is this really not a problem in practice (say x86, x64 and ARM)?
This is against everything I learned, but he has a long beard and a really smart look!
Extra points if you can show me the piece of code proving that it is wrong!