This forum is now closed.
This example is a bit puzzling. I have looked for an explanation on forums but I could not find anything. This is the example illustrating pitfalls with flush.
As far as I can tell, flag at any given time can only be equal to 0 or 1. Because of the while condition on line 29, flag must be 1 on line 34. How can it be undefined?
Similarly, if flag must be 1 on line 34, data=42 must have been written to memory by thread 0 (line 18). The flush by thread 1 (line 28 or 31) must occur after line 20 has executed. Therefore thread 1 must be able to see the updated value of data.
This reasoning is contradictory with the statement in the specification:
"The following example demonstrates why synchronization is difficult to perform correctly through variables. The value of flag is undefined in both prints on thread 1 and the value of data is only well-defined in the second print."
Could anyone explain this?
You're not the first person to say that!muphof wrote:This example is a bit puzzling
This seems reasonable, but is only true if writes are assumed to be atomic, which OpenMP memory model (deliberately) does not do, because themuphof wrote:As far as I can tell, flag at any given time can only be equal to 0 or 1.
data types for which writes are atomic can very between platforms. In practice, on most hardware, writes of 32-bit integers are atomic, but in principle they might not be, and therefore a read of a variable while another thread is in the process of writing it could return an undefined value which is neither 0 nor 1.
The value of flag is undefined in both prints on thread 1 because it cannot be guaranteed that any the flushes of flag on thread 1 happened after the final flush of flag on thread 0.
Hope that helps,
Thank you for your explanation. It does help answer the first part of my question. I understand that if writes and reads are not atomic, the value of flag is indeed undefined.
However let us consider the case of data=42 now. The specification claims that data is undefined at line 34. Let's see if this is possible.
For thread 1 to reach line 34, thread 0 must have started executing line 20. This is true even if we assume non-atomic read/writes. Looking at this more closely, there is a strict sequential order (if we assume non-atomic read/writes, the order is only for starting the instruction not for completing it):
thread 0: line 18
thread 0: line 20 // Line 18 must be complete. At that point data=42 in memory and thread 1 will get that value at its next flush
thread 1: line 28 or 31 and flag becomes non-zero for thread 1
The previous state cannot occur before line 18 is complete. Therefore if the flush by thread 1 on line 28 or 31 returns a flag that is not zero, it must also force thread 1 to correctly read the new value of data=42.
Thank you for looking more into this.
A possible sequence of events is:
thread 1: flush (line 28)
thread 0: data = 42 (line 14)
thread 0: flush (line 18)
thread 0: flag = 1 (line 20)
thread 1: while (flag < 1) test fails (line 29)
thread 1: read data (line 34)
which fails to satisfy the required write(0)-flush(0)-flush(1)-read(1) ordering (see top of page 18 of 4.0 RC2).
After the flush on line 35, the ordering is now guaranteed, and data is then valid.
You're very welcome!Thank you for looking more into this.
I guess my only advice would be to add a few lines on p. 224 to explain this. For example we could mention the following:
The following example demonstrates why synchronization is difficult to perform correctly through variables. The value of flag is undefined in both prints on thread 1 and the value of data is only well-defined in the second print. The value of flag is undefined because read/writes are not guaranteed to be atomic. Therefore the value read by thread 1 is undefined. The value of data is undefined in the first print because it is not possible to guarantee that either flush by thread 1 on line 28 or 31 happens after the flush by thread 0 on line 18 completes. In particular flag may be read by thread 1 without ever executing the flush on line 31. However, the second flush on line 35 necessarily gets the updated value for data.