I'm trying to understand the Happens Before mechanism and i can't find any source in the internet talking about happens-before relationship between a READ and subsequent WRITE. I can only read about the opposite: hb(w,r).
Anyhow reading the JLS i found that:
We say that a read r of a variable v is allowed to observe a write w to v if, in the happens-before partial order of the execution trace:
r is not ordered before w (i.e., it is not the case that hb(r, w))
It also says:
A set of actions A is happens-before consistent if for all reads r in A, where W(r) is the write action seen by r, it is not the case that either hb(r, W(r))
I don't understand this hb(r,w) relationship, what is that and how does it work? What is trying to say the JLS?
Thanks!
hb(r, w)describes the opposite ofhb(w, r). Let's start with the easier to followhb(w, r)that you already seem to understand. Let me write it in my own words, so that when I explainhb(r, w), you can see where it's almost exactly the same explanation:BARis the first line in therun()method of a thread, andFOOist.start(), FOO/BAR have a happens-before relationship in the JMM sense.This means that it must never be the case that BAR gets to observe any state as it was before FOO modified it. In other words, if some field
xis 5, andFOOisx=10;, then ifBARobservesxbeing 5, then that JVM is broken.The texts tend to talk about 'observability' because that's more accurate than the far more obvious but subtly wrong: "If
hb(FOO, BAR), thenBARis guaranteed to run afterFOO" - because if BAR doesn't observe anything FOO does, then the JVM can completely ignore the hb relationship there and reorder at will. This seems irrelevant (if it is not possible to observe which way a thing was ordered, then who cares?), but it is relevant: "observe" is also a very specific term and it doesn't mean what the english dicionary says it means. For example, "observing" that things are happening by way of how long the CPU needs to process it is in the human/dictionary definition certainly "observing", but not in the JMM's definition of "observing".For example, I don't think
System.currentTimeMillis()is specced to be an HB/HA point (I think on most impls it ends up being one - it's a lot slower than you think, but that's an implementation detail), so ifFOOandBARboth log their acts, in theory one can notice thatBARoccurs beforeFOOeven thoughhb(FOO, BAR)- as long asBARdoesn't 'observe' any fields that FOO modified, that's allowed. You'll have a hard time finding a code+JVMimpl+OS+architecture combo to actually see that happening, but you managed to find such a combo, nothing is broken - bugs you file with that as example would be closed as 'working as intended'. The JVM reserves the right to do that; it's on you to write code that is aware it can happen; if you don't - your code is broken. Probably in a way that will never actually result in a problem on your current JVM stack, but broken nonetheless.One could argue that it is possible to witness that
BARoccurred beforeFOOvia those timestamps, i.e. that it can be 'observed', but that's not the meaning of the word "observe" that the JVM implies in this section.And now to flip it around
What
hb(r, w)is about is the reverse. Given that the JVM is allowed to reorderFOOandBAReven ifhb(FOO, BAR)is established, because the only actual guarantee the JVM makes is thatBARcannot observe state as it was beforeFOOmodified any of it (i.e. BAR cannot read anything that predates FOO's writes), we get into this funky scenario:Well, then, if we flip it around and it is in fact
FOO(the 'before' bytecode) that writesx = 10;and it's theBAR(the 'after') code that attempts to observe (read)x, given that the JVM explicitly does not define happens-before as actually requiring that FOO runs physically before BAR, is reordering then allowed and thus a JVM may wel lreorder things so that BAR gets to observe the state ofxas it is after FOO writes to it, i.e., ifBARreads the value ofxas10, then that's fine, the JVM isn't broken?That section about
hb(r, w)says: No. Given thatxis5originally and some statement modifies it to10, the JMM makes BOTH guarantees:x = 10;is JMM-defined as 'happening before' a read x, the read code cannot observe the 5.x = 10;is JMM-defined as 'happening after' a read x, the read code cannot observe the 10.x = 10;is JMM-defined as having no HB/HA relationship to code that reads x, then the read code can observe either5or10- a JVM is allowed to do either one, depending on whatever it wants including the phase of the moon. There is no guarantee of consistency either. It is allowed to flip a coin every run if the JVM wants to. However, a JVM is not allowed to let the read code observe anything else. pre- state is fine, post-state is fine, but nothing else1.[1] As per the spec this guarantee does not exist for
long/double(you can see 'sheared' updates - the top 32 bits having been set to the new value with the bottom 32 bits still on the old, or vice versa, as per the JVM spec). However, and this is a personal injection: As far as I know, no 64-bit JVM implementation exists where you would be able to observe sheared long/double updates. 32-bit hardware still exists of course. Raspberry π's are a thing, for one.