Yeah, I also can't dream up a scenario where that would be justified.
As to what is allowed in context of conforming to the standard... The specific example takes an extreme, unwarranted liberty that has noticeable side effects and performance implications. That's a defect. A bug.
Please note that this is NOT strong enough for mutual-exclusion such that you cannot use these memory barrier guarantees to build Petersons Algorithm. You would need to insert a stronger membar (e.g., MEMBAR #StoreLoad | #StoreStore) in order to prevent subsequent load from hoisting up above the previous store in the lock acquire portion of the algorithm. However, it is strong enough for a DCL algorithm or a producer/consumer example: _______________________________________________________________ int data = 0; atomic_word volatile g_flag = 0;
void single_producer() { data = 1234; g_flag = 1; }
However, these barriers are actually too strong here such that the #LoadStore restrain is not required for the example... On the SPARC you could write it as: _______________________________________________________________ int data = 0; atomic_word g_flag = 0;
Even then, the barriers can be too strong for architectures that support implicit data-dependant load barriers (e.g., all but the DEC Alpha). You can write it this way: _______________________________________________________________ int data = 0; int* g_flag = 0;
void single_producer() { data = 1234; g_flag = &data; }
I don't agree. There are lots of ways that the Standard gives license to implementations to do extreme, absurd, or ridiculous things, but practically speaking the effects are minimal. For example, there's nothing stopping an implementation from choosing sizeof(int) == 1000000000. It's hard to grant /some/ freedoms like this to implementations without also allowing extreme choices that seem ridiculous.
Should the ridiculous choices be forbidden (even assuming that suitable language could be found to do that)? I would say no, because what seems ridiculous in one context might very well have useful implications in another context.
Where I think the Standard falls short here is in explaining what it is that must be documented when an implementation adopts a particular "volatile access" model. Also, the question of what consequences must follow given a particular choice of memory model, which I believe the Standard /does/ mean to specify, is not spelled out very clearly in the current Standard text.
I didn't say that wasn't conforming. Whether it is or isn't is a separate issue. I said I consider it a bug, a product defect, and if uncorrected makes it unusable for my needs.
Did we both misunderstand each other? I was talking about whether or not the Standard should be considered to have a bug (and in this regard I don't think it should), not whether the example implementation should be considered to have a bug.
In fact, I wouldn't say this particular implementation decision merits the term "bug" either. It may be a poor decision in terms of your needs. Possibly it's a poor decision in terms of most other people's needs also. But as long as the implementation is conforming and does what its developers intended, at worst the "feature" represents poor judgment. And who knows, it may be just what someone is looking for, and they may be quite glad to find an implementation that implements volatile "correctly".
I take your point, although I wouldn't use the term "correctness" to label the attribute that (I think) you mean to reference. Perhaps "quality" or "appropriateness" or something along those lines. "Correctness" is measured relative to a specification, but what (I think) you're talking about is some sort of independent judgment, which has no specification.
In C code such as device drivers, one often encounters code like:
while (ADDR->c_reg&BUSY) ;
where one is testing bits of a hardware device register, and waiting for a change.
In there anything in the C specification which would prevent a good (but obviously not perfect) optimizer from reading the value of the device register just once, stuffing it into a CPU general purpose register and looping on its value ? I have noticed that at least some of the 4xbsd makefiles seem to purposely avoid the optimizer - is this sort of thing the reason ? Might not C benefit from some additional type (e.g. "unregister") which would tell an optimizer what was up. I believe DEC's MicroPower Pascal has a type called "volatile" which prevents this sort of thing.
Or whatever criteria the judger chooses -- that's why it's an /independent/ judgment (and explains why there is no specification, because each judger makes their own decision about what requirements should be satisfied).
There are two pertinent aspects that may be considered relevant here: the choice of term ('volatile'), and what it does (ie, the intended meaning, or what it's for).
For choice of term, I think the ordinary English meaning is close enough so that it's reasonable to consider the ordinary word as the origin of the term. That it was used earlier in another language simply means they found the standard meaning close enough for their purposes -- no different than 'if', 'break', 'while', etc.
For what it does, people more familiar with the history than I am have said (and I believe there's a fair amount of truth in this) that members of the committee were divided between two related but distinct meanings/uses, namely, for accessing special memory locations or hardware registers, and for inhibiting optimization for some sort of inter-process sychronization. Rather than settle on a single purpose, the two different uses were conflated into a single one, both brought into play using the keyword 'volatile'. So even if the earlier precedent may have played a role in motivating a need, I think 'volatile' in its current form has a broader base than just the "we need to access a special memory location" kind of purpose. Remember, language features put in to enable/inhibit various low-level machine details weren't new even in the 1980's. To give just one example, PL/I had such things going back to the 1960's.
Aren't these the same thing: To inhibit optimizations so as enforce memory access? Is the distinction to be made between sharing the memory with another process vs. a piece of hardware?
Rather than
That's so much clearer: nice and vague with an anecdotey feel.
I know I'd shouting from the back-row, here; so feel free to be as curt as you wish. I really don't know what I'm talking about, never having used the thing. But I thought I had this pretty-well gelled; now it's all runny.
I suppose they could be, depending on how generically the terms were meant. In the particular case, I don't think they were, because of different assumptions about what determines the behavior on the other accesses. For hardware memory registers, it could be just about anything; for inter-process synchronization, the natural assumption would be that the other process would also be a C program (and so would view 'volatile' in the same way as this program). There's a big difference between those two.
Like I said, I wasn't there, but that's basically how I took the comments about what actually happened.
I think you need to make up your mind. If what you mean by "correctness in the implementation" depends on implementing volatile in the way the Standard says, then it is not orthogonal to "conformance to the standard". Either the two conditions /are/ orthogonal, in which case judging correctness is independent of the Standard, or judging correctness depends on the Standard, in which case they are /not/ orthogonal. It can't be both. Or maybe you mean something different by "orthogonal" than how the term is usually meant? If so maybe you could explain that.
If the implementation can conform to the standard and not generate the desired result, conformance is orthogonal to correctness. Isn't that your argument? That implementation-defined means anything and everything? (Don't bother. That was rhetorical. I'm outa this one.)
[#2] Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects,10) which are changes in the state of the execution environment. Evaluation of an expression may produce side effects. At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place. (A summary of the sequence points is given in annex C.)
This implies that all writes are side-effects, as are reads of volatile objects.
If buffer originally contained non-zero values, and another thread was monitoring its contents via a "volatile char *", it should be guaranteed to see the elements being cleared in ascending order, with buffer_ready only being set after all elements of buffer were cleared.
ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here.
All logos and trade names are the property of their respective owners.