The Semantics of 'volatile'

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.

Reply to
MikeWhy
Loading thread data ...

FWIW, MSVC compilers, versions 8 and above, automatically insert load-acquire/store-release barriers for volatile loads and stores on certain architectures (e.g., PowerPC): _______________________________________________________________ void* volatile_load( void** p ) { void* v = ATOMIC_LOAD(p); MEMBAR #LoadStore | #LoadLoad; return v; }

void* volatile_store( void** p, void* v ) { MEMBAR #LoadStore | #StoreStore; ATOMIC_STORE(p, v); return v; } _______________________________________________________________

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; }

void multiple_consumers() { while (! g_flag) backoff(); assert(data == 1234); } _______________________________________________________________

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;

void single_producer() { data = 1234; MEMBAR #StoreStore; g_flag = 1; }

void multiple_consumers() { while (! g_flag) backoff(); MEMBAR #LoadLoad; assert(data == 1234); } _______________________________________________________________

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; }

void multiple_consumers() { while (! g_flag) backoff(); assert(*g_flag == 1234); } _______________________________________________________________

Reply to
Chris M. Thomasson

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.

Reply to
Tim Rentsch

Thank you! An excellent set of examples.

Reply to
Tim Rentsch

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.

Reply to
MikeWhy

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".

Reply to
Tim Rentsch

I think we're agreeing. Conformance to the standard is orthogonal to correctness in the implementation.

Reply to
MikeWhy

MikeWhy wrote: ...

Conformance with the standard is part of correctness, not orthogonal to it, at least for any compiler which is intended to implement that standard.

Reply to
James Kuyper

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.

Reply to
Tim Rentsch

ntext

k

If

PIC?

ied.

The

.

ms

arate

ted

Perhaps according to the "right thinking" of the eightfold path?

Now that you're awake, this gem from the archive might be interesting. It appears to be the genesis of "volatile".

-- \nlxt

Message-ID: Newsgroups: net.lang.c Path: utzoo!decvax!cca!ima!n44a!dan X-Path: utzoo!decvax!cca!ima!n44a!dan From: n44a!dan Date: Thu Apr 28 05:47:51 1983 Subject: C and real hardware Posted: Wed Apr 27 15:34:30 1983 Received: Thu Apr 28 05:47:51 1983

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.

Dan Ts'o

Reply to
luserXtrog

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.

Reply to
Tim Rentsch

o

Solid.

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.

-- lxt

-- lxt

Reply to
luserXtrog

No more so than any other part of the standard. "Volatile" means "volatile", not "do what you damned well please".

Reply to
MikeWhy

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.

Reply to
Tim Rentsch

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.

Reply to
Tim Rentsch

Are you both referring to the following paper?

"Volatiles Are Miscompiled, and What to Do about It"

formatting link

Regards.

Reply to
Boon

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.)

Reply to
MikeWhy

That is the one I meant. Their first example (2.1) is wrong I think:

======================================================================

volatile int buffer_ready; char buffer[BUF_SIZE]; void buffer_init() { int i; for (i=0; i

Reply to
John Devereux

Hi Tim,

Its been said that volatile is the multithreaded programmers best friend.

Cheers, Joe

--
       ......................  o    _______________           _,
      ` Good Evening!  ,      /\_  _|             |        .-'_|
      `................,     _\__`[_______________|       _| (_|
                             ] [ \, ][         ][        (_|
Reply to
kid joe

...

[#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.

Reply to
Nobody

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.