You're right, but not because of the way the code is written. Rather, it allows for a data race because of the potential for the statements to be reordered. Executed in the order written, it still illustrates how unsynchronized code, even when it is not reordered, can produce inconsistent results. In fact, making s volatile doesn't correct for that. (Indeed, that's not necessarily even a bad thing: using a shared variable that is read by one thread and written by another thread is the accepted way for the writing thread to signal the reading thread that the latter is to stop.)
So, yeah, it's a data race, but only if you consider reordering. You don't need reordering for that code to illustrate the problem it illustrates. (To be more thorough: you don't need reordering for the write in one thread to be invisible to another, because memory caching may fail to make the last-written copy of s available to the reading thread. That's another reason to make s volatile, but I don't think that meets the definition of a data race. I could be wrong about that, though.)
Fascinating discussion. Very informative (well, to me, anyway). Marking this thread "resolved," although I'd be happy to read more here on this topic.
"Il y a peu de choses qui me soient impossibles..."