Simultaneous Writes on Volatile

S

Shao Miller

Does a C Standard prevent a conforming implementation from scheduling
simultaneous writes on a 'volatile'?

Assuming not, and further assuming a particular scenario where there is
no unknown factor to modify the 'volatile' array below, is the output of
the following program well-defined to be one of: { 0, 5, 9 }? Or is the
output unspecified or implementation-specific?

#include <stdio.h>

int main(void) {
volatile int arr[2];

arr[0] = 1;
arr[1] = 5;
arr[0] = arr[arr[0]] + --(arr[arr[0] - 1]) + arr[arr[0]];
printf("%d\n", arr[0]);
return 0;
}
 
S

Shao Miller

Shao Miller wrote:
Shao said:
Does a C Standard prevent a conforming implementation from scheduling
simultaneous writes on a 'volatile'?

Assuming so, and further assuming a particular scenario where there is
no unknown factor to modify the 'volatile' array below, is the output of
the following program well-defined to be one of: { 0, 5, 9 }? Or is the
output unspecified or implementation-specific?

#include <stdio.h>

int main(void) {
volatile int arr[2];

arr[0] = 1;
arr[1] = 5;
arr[0] = arr[arr[0]] + --(arr[arr[0] - 1]) + arr[arr[0]];
printf("%d\n", arr[0]);
return 0;
}
I believe that 'n1256.pdf', 6.7.3p6 makes it implementation-defined, but
would appreciate other opinions.
 
W

Walter Banks

Shao said:
Shao Miller wrote:
Shao said:
Does a C Standard prevent a conforming implementation from scheduling
simultaneous writes on a 'volatile'?

Assuming so, and further assuming a particular scenario where there is
no unknown factor to modify the 'volatile' array below, is the output of
the following program well-defined to be one of: { 0, 5, 9 }? Or is the
output unspecified or implementation-specific?

#include <stdio.h>

int main(void) {
volatile int arr[2];

arr[0] = 1;
arr[1] = 5;
arr[0] = arr[arr[0]] + --(arr[arr[0] - 1]) + arr[arr[0]];
printf("%d\n", arr[0]);
return 0;
}
I believe that 'n1256.pdf', 6.7.3p6 makes it implementation-defined, but
would appreciate other opinions.

Most of the literature on volatile is assuring that it is
always read when referenced. I have seen very little mention
from either ISO or our customers on delayed writes to a
volatile


Regards,


w..
 
E

Eric Sosman

Does a C Standard prevent a conforming implementation from scheduling
simultaneous writes on a 'volatile'?

I think you'll need to define "simultaneous" before this question
can be answered. Also, note that each implementation makes its own
definition of what it means to access a volatile object, hence of what
it means to write such an object.
Assuming not, and further assuming a particular scenario where there is
no unknown factor to modify the 'volatile' array below, is the output of
the following program well-defined to be one of: { 0, 5, 9 }? Or is the
output unspecified or implementation-specific?

If you're assuming no external modification, I don't see how
`volatile' enters into the question. Accessing a volatile variable
does not introduce a sequence point, if that's what you're getting at.
#include <stdio.h>

int main(void) {
volatile int arr[2];

arr[0] = 1;
arr[1] = 5;
arr[0] = arr[arr[0]] + --(arr[arr[0] - 1]) + arr[arr[0]];

Looks like undefined behavior to me, for the same reason
discussed (at wearying length) in the "C Test Incorrectly Uses
printf() - Please Confirm" thread.
 
S

Shao Miller

Walter said:
Shao said:
Shao Miller wrote:
Shao said:
Does a C Standard prevent a conforming implementation from scheduling
simultaneous writes on a 'volatile'?

Assuming so, and further assuming a particular scenario where there is
no unknown factor to modify the 'volatile' array below, is the output of
the following program well-defined to be one of: { 0, 5, 9 }? Or is the
output unspecified or implementation-specific?

#include <stdio.h>

int main(void) {
volatile int arr[2];

arr[0] = 1;
arr[1] = 5;
arr[0] = arr[arr[0]] + --(arr[arr[0] - 1]) + arr[arr[0]];
printf("%d\n", arr[0]);
return 0;
}
I believe that 'n1256.pdf', 6.7.3p6 makes it implementation-defined, but
would appreciate other opinions.

Most of the literature on volatile is assuring that it is
always read when referenced. I have seen very little mention
from either ISO or our customers on delayed writes to a
volatile
Thank you for your kind response. In such a case where the '--'
performs its modification access right away, could it possibly collide
with a scheduled read of the same object, or would you schedule all
'volatile' accesses one-at-a-time, to avoid such a possible collision?
 
S

Shao Miller

Eric said:
I think you'll need to define "simultaneous" before this question
can be answered. Also, note that each implementation makes its own
definition of what it means to access a volatile object, hence of what
it means to write such an object.
By "simultaneous," I meant "at the same time." We have some reads and
writes to do, but I'm pondering a system where the write for the '--'
decrement might be scheduled with the write from the '=' assignment, or
if a C Standard implies that volatile accesses should be all performed
one-at-a-time. I'm trying to figure out what a conforming
implementation is within its rights to define, and beyond that, what the
sensible thing to do is.
If you're assuming no external modification, I don't see how
`volatile' enters into the question. Accessing a volatile variable
does not introduce a sequence point, if that's what you're getting at.
No, I wasn't getting at that. I'm trying to understand the intentions
of a C Standard in regards to 'volatile'. On the one hand, it suggests
that if an implementation's semantics correspond one-to-one with the
abstract semantics, then 'volatile' would be redundant[1]. On the other
hand, implementations define both what constitutes an access for a
'volatile' object[2] as well as the order of side effects between
sequence points[3].

So is a conforming implementation within its rights to completely
discard 'volatile' altogether? We see details that 'register' and
'restrict' are hints; is 'volatile' such a hint, just the same? If so,
is it a hint about anything within the definitions of a C Standard?

There is a non-normative footnote[4] that details actions on 'volatile's
not being "optimized out" or re-ordered. If the implementation defines
order and access, what is the hint? Does it mean, in general, "be extra
careful?"
#include <stdio.h>

int main(void) {
volatile int arr[2];

arr[0] = 1;
arr[1] = 5;
arr[0] = arr[arr[0]] + --(arr[arr[0] - 1]) + arr[arr[0]];

Looks like undefined behavior to me, for the same reason
discussed (at wearying length) in the "C Test Incorrectly Uses
printf() - Please Confirm" thread.
It's clear from the reference you pointed out in that thread[5] that if
the big expression above modifies some object and tries to do so twice
within the same sequence point bounds, that it's outside of the C Standard.

If one were to develop an implementation that catches all undefined
behaviour and reports it (I think there's a name for such a theoretical
implementation), the 'volatile' qualifier might prevent a
translation-time proof that the big expression above tries to access the
same object twice, since unknown factors could cause the indices to
change and result in well-defined behaviour.

In other words, it seems to be a messy situation. If 'volatile' is an
implementation-specific hint, could we discard it? If it has some
deeper meaning, can the big expression above only be determined to be
undefined behaviour based on both the implementation's definitions as
well as only at run-time? Or would the expression be a case of
"undefined by omission" rather than as a violation of the "shall" in 6.5p2?

Thanks very much for your response.

References from the "C99" C Standard draft with filename 'n1256.pdf':
[1] 5.1.2.3p8
[2] 6.7.3p6
[3] 6.5p3
[4] 6.7.3, footnote 116
[5] Its counterpart in the draft: 6.5p2
 
S

Shao Miller

Big said:
Volatile means the variable may be modified by some agent unknown to the
compiler, therefore the compiler must load the variable on each reference. It
doesn't mean the compiler can scramble the code it does see.
Thanks for your response. I agree with your first statement. Now if
the implementation defines the order of evaluation and side effects,
what's there to scramble, exactly?
 
E

Eric Sosman

By "simultaneous," I meant "at the same time."

(Sigh.) All right, now define "time" and "same," grounding
both definitions in terms already defined by the Standard.
We have some reads and
writes to do, but I'm pondering a system where the write for the '--'
decrement might be scheduled with the write from the '=' assignment, or
if a C Standard implies that volatile accesses should be all performed
one-at-a-time. I'm trying to figure out what a conforming implementation
is within its rights to define, and beyond that, what the sensible thing
to do is.

A conforming implementation must (among other things) define
what an "access" to a volatile-qualified object is. A conforming
implementation is not obliged to sequence things one way or another
simply because there's a `volatile' lurking somewhere about.
If you're assuming no external modification, I don't see how
`volatile' enters into the question. Accessing a volatile variable
does not introduce a sequence point, if that's what you're getting at.
No, I wasn't getting at that. I'm trying to understand the intentions of
a C Standard in regards to 'volatile'. On the one hand, it suggests that
if an implementation's semantics correspond one-to-one with the abstract
semantics, then 'volatile' would be redundant[1]. On the other hand,
implementations define both what constitutes an access for a 'volatile'
object[2] as well as the order of side effects between sequence points[3].

You're right about [1] and [2], but completely wrong about [3].
The quoted text says the ordering of side-effects is "unspecified,"
not "implementation-defined." The implementation is not obliged to
define any ordering for side-effects. Corollary: The implementation
is not required to use a consistent ordering, not even from one
evaluation of the same source expression to the next.
#include <stdio.h>

int main(void) {
volatile int arr[2];

arr[0] = 1;
arr[1] = 5;
arr[0] = arr[arr[0]] + --(arr[arr[0] - 1]) + arr[arr[0]];

Looks like undefined behavior to me, for the same reason
discussed (at wearying length) in the "C Test Incorrectly Uses
printf() - Please Confirm" thread.
It's clear from the reference you pointed out in that thread[5] that if
the big expression above modifies some object and tries to do so twice
within the same sequence point bounds, that it's outside of the C Standard.

True, but that's not what the referenced thread is about. The
objectionable expression attempts to modify an object and to use it
for purposes other than determining the new value, and that's that.
(Since the behavior is undefined, it's not possible to tell for sure
whether the expression attempts to modify one object twice.)
If one were to develop an implementation that catches all undefined
behaviour and reports it (I think there's a name for such a theoretical
implementation),

The name you're searching for is "nonexistent," or maybe even
"impossible." (The exhibition of a counter-example would be a boon
to the practice of programming, should you feel so inclined.)
In other words, it seems to be a messy situation. If 'volatile' is an
implementation-specific hint, could we discard it?

There's a footnote that says as much -- and what's more, you've
read the footnote.
 
S

Shao Miller

Eric said:
(Sigh.) All right, now define "time" and "same," grounding
both definitions in terms already defined by the Standard.
I meant "simultaneous" as outside of a C Standard. Posters have offered
that one potential reason why the Standard doesn't define side effect
order within sequence point bounds is because there is or could be
hardware where a simultaneous read and write to the same object could
cause an exception. If that's a relevant reason, then so should this
question about simultaneity be relevant. Perhaps it's out-of-scope for
what you're interested in discussing. If so, I readily accept that.
Perhaps one of the posters who offered that reason might have something
to say about the matter.
A conforming implementation must (among other things) define
what an "access" to a volatile-qualified object is. A conforming
implementation is not obliged to sequence things one way or another
simply because there's a `volatile' lurking somewhere about.
Aha. Ok. Where "one way" might be "simultaneously". So a conforming
implementation has _two_ chances to discard 'volatile'.

volatile int a = 1;
a = a + a;

1. "I define the actual semantics to be one-to-one with the abstract
semantics. 'volatile' is redundant."

2. "I define that all reads of objects within the same sequence point
bounds shall occur no more than once per object. At each sequence
point, I will read all values for all objects read within an expression
no more than once. I will pend all modifications for immediately before
the next sequence point; these will be scheduled to occur simultaneously."

Seem ok?
Assuming not, and further assuming a particular scenario where there is
no unknown factor to modify the 'volatile' array below, is the
output of
the following program well-defined to be one of: { 0, 5, 9 }? Or is the
output unspecified or implementation-specific?

If you're assuming no external modification, I don't see how
`volatile' enters into the question. Accessing a volatile variable
does not introduce a sequence point, if that's what you're getting at.
No, I wasn't getting at that. I'm trying to understand the intentions of
a C Standard in regards to 'volatile'. On the one hand, it suggests that
if an implementation's semantics correspond one-to-one with the abstract
semantics, then 'volatile' would be redundant[1]. On the other hand,
implementations define both what constitutes an access for a 'volatile'
object[2] as well as the order of side effects between sequence
points[3].

You're right about [1] and [2], but completely wrong about [3].
The quoted text says the ordering of side-effects is "unspecified,"
not "implementation-defined." The implementation is not obliged to
define any ordering for side-effects. Corollary: The implementation
is not required to use a consistent ordering, not even from one
evaluation of the same source expression to the next.
Uhh... If the Standard doesn't define the ordering of side effects and
the implementation doesn't, then who does? Even a random side effect
order is a definition.

I think what you're getting at is that it doesn't need to be documented
by the implementation. If so, then I agree.

Unfortunately, the constraint of using terms exactly as they appear in
the Standard makes it confusing if I type "implementation-defined".
What I should have typed was "implementation-chosen". Sorry about that!
#include <stdio.h>

int main(void) {
volatile int arr[2];

arr[0] = 1;
arr[1] = 5;
arr[0] = arr[arr[0]] + --(arr[arr[0] - 1]) + arr[arr[0]];

Looks like undefined behavior to me, for the same reason
discussed (at wearying length) in the "C Test Incorrectly Uses
printf() - Please Confirm" thread.
It's clear from the reference you pointed out in that thread[5] that if
the big expression above modifies some object and tries to do so twice
within the same sequence point bounds, that it's outside of the C
Standard.

True, but that's not what the referenced thread is about. The
objectionable expression attempts to modify an object and to use it
for purposes other than determining the new value, and that's that.
(Since the behavior is undefined, it's not possible to tell for sure
whether the expression attempts to modify one object twice.)
Right, in regards to the other thread. But in the big expression above,
'arr[0]' is being used to determine the new value. The '--'
modification side effect might or might not be on the same object,
depending on the circumstances, and might not be known one way or the
other until run-time.

So I'll ask if we can really call it undefined behaviour by looking at
it. I think in this case that it's implementation-chosen... A
combination of factors including implementation-defined and unspecified,
as you've mentioned. As in, 6.5p2 cannot apply here (above) until after
those bits are known, right?
The name you're searching for is "nonexistent," or maybe even
"impossible." (The exhibition of a counter-example would be a boon
to the practice of programming, should you feel so inclined.)
Ha! Nice one. :) I was thinking of a particular implementation acronym
that's been passed around a bit and that's a bit too dreadful for me to
repeat. But yes, such an implementation could make short work of
assumptions that fall outside of a C Standard... If certain tricky
details could be clarified.
There's a footnote that says as much -- and what's more, you've
read the footnote.
Well, the non-normative footnote is for "information only", whereas
'register' and 'restrict' are normative, so it's worth asking opinions on.

Additionally, 'register' has some Standard-defined consequences. Is
that so for 'volatile'? I think you're stating that 'volatile' does not
have Standard-defined consequences.

Thank you, as always.
 
E

Eric Sosman

I meant "simultaneous" as outside of a C Standard.

Then your question "Does a C Standard prevent [matters that
are] outside a C Standard?" is easily answered in the negative.
 
F

FredK

Shao Miller said:
Snip

No, I wasn't getting at that. I'm trying to understand the intentions of
a C Standard in regards to 'volatile'. On the one hand, it suggests that
if an implementation's semantics correspond one-to-one with the abstract
semantics, then 'volatile' would be redundant[1]. On the other hand,
implementations define both what constitutes an access for a 'volatile'
object[2] as well as the order of side effects between sequence points[3].

So is a conforming implementation within its rights to completely discard
'volatile' altogether? We see details that 'register' and 'restrict' are
hints; is 'volatile' such a hint, just the same? If so, is it a hint
about anything within the definitions of a C Standard?

Snip.

I'm not a standards guy, however I've written a lot of low-level C code for
a very long time. In my experience, the "intention" of volatile is to
provide the compiler an implementation-specific means of accessing hardware
and shared memory. Requiring that every reference to the volatile location
actually causes the location itself to be read or written is one part of
volatile that is universal, but there are many other things that compilers
may/will do/guarantee that are implementation (and HW) specfic. Using
volatile for sharing a memory location between multiple threads of operation
also is a critically important use - but volatile in and of itself doesn't
provide all of the semantics and mechanics of doing that operation
correctly - it is just one piece of the puzzle.

The vast majority of C programmers will never have a need to use or
understand the specifics of volatile. The very few percentage of us who do
need it, know the specific guarantees that the C implementation that we are
using provide us. The original example usage that started this discussion
was a nonsense usage, and would never have been written by anyone that had a
clue as to what they were doing... Despite esoteric attempts to provide
some rationale for it's usage in the example, the end result for the example
would simply be to defeat some optimization and cause extra fetches of the
variable for no good reason.

Real world examples of volatile enables code such as:

time_1 = *the_clock;
....
time_2 = *the_clock;

to correctly work. And for the typical C programmer, such usage never comes
up. The typical C program doing the above would instead be calling a system
service such as getttime.
 
S

Shao Miller

Eric said:
On 8/15/2010 4:15 PM, Shao Miller wrote:
Does a C Standard prevent a conforming implementation from scheduling
simultaneous writes on a 'volatile'?
Snip

No, I wasn't getting at that.  I'm trying to understand the intentions of
a C Standard in regards to 'volatile'.  On the one hand, it suggests that
if an implementation's semantics correspond one-to-one with the abstract
semantics, then 'volatile' would be redundant[1].  On the other hand,
implementations define both what constitutes an access for a 'volatile'
object[2] as well as the order of side effects between sequence points[3].
So is a conforming implementation within its rights to completely discard
'volatile' altogether?  We see details that 'register' and 'restrict' are
hints; is 'volatile' such a hint, just the same?  If so, is it a hint
about anything within the definitions of a C Standard?

Snip.

I'm not a standards guy, however I've written a lot of low-level C code for
a very long time.  In my experience, the "intention" of volatile is to
provide the compiler an implementation-specific means of accessing hardware
and shared memory. Ok.

 Requiring that every reference to the volatile location
actually causes the location itself to be read or written is one part of
volatile that is universal, but there are many other things that compilers
may/will do/guarantee that are implementation (and HW) specfic. Sure.

 Using
volatile for sharing a memory location between multiple threads of operation
also is a critically important use - but volatile in and of itself doesn't
provide all of the semantics and mechanics of doing that operation
correctly - it is just one piece of the puzzle. Agreed.

The vast majority of C programmers will never have a need to use or
understand the specifics of volatile.
Sure. And people curious about C implementation design decisions
might.
 The very few percentage of us who do
need it, know the specific guarantees that the C implementation that we are
using provide us. Right.

 The original example usage that started this discussion
was a nonsense usage,
If I have written code that offends you, then I apologize. Please
understand its purpose. Its purpose is not as code that one might see
in a practical program. Its purpose is to provide a reference during
discussion. Implementations will do something with that program.
What can we expect due to conformance? What can we expect is
implementation-specific? What exactly does a C Standard suggest about
'volatile'? These are questions. I appreciate you sharing your
thoughts on the matter.
and would never have been written by anyone that had a
clue as to what they were doing...  Despite esoteric attempts to provide
some rationale for it's usage in the example, the end result for the example
would simply be to defeat some optimization and cause extra fetches of the
variable for no good reason.
There doesn't need to be any rationale. It's C code and an
implementation must do something with it. A C Standard might give us
some expectations. Outside of that, experience and specific
implementation knowledge might give us some expectations. If you
really require an example that suits you, that's fine. Some people
enjoy minimal, abstract examples. Some people enjoy practical
examples. That's fine.
Real world examples of volatile enables code such as:

time_1 = *the_clock;
...
time_2 = *the_clock;

to correctly work.  And for the typical C programmer, such usage never comes
up.  The typical C program doing the above would instead be calling a system
service such as getttime.
Sure.
 
S

Shao Miller

   arr[0] = arr[arr[0]] + --(arr[arr[0] - 1]) + arr[arr[0]];

Throw everything else aside ... DON'T WRITE CODE LIKE THIS.

Even if it were valid C I'd still reject it in a code review effort.
It's valid C. You're dismissing the questions. You're right about
avoidance of typing code like this. Great; a good reminder.
 
L

lawrence.jones

Shao Miller said:
I'm trying to understand the intentions
of a C Standard in regards to 'volatile'.

The only intention is to provide a standard hook (along with a big
loophole) to allow an implementation to do "the right thing" -- whatever
that is in its environment -- to support memory mapped I/O, shared
memory, or whatever.
 
L

lawrence.jones

Shao Miller said:
Well, the non-normative footnote is for "information only", whereas
'register' and 'restrict' are normative, so it's worth asking opinions on.

Please don't fall into the trap of thinking that informative elements
are any less valuable than normative elements or can be ignored. We put
them in because we believe them to be true (but redundant). If they're
not, that's indicative of a defect in the normative part of the standard.
 
T

Tom St Denis

   arr[0] = arr[arr[0]] + --(arr[arr[0] - 1]) + arr[arr[0]];
Throw everything else aside ... DON'T WRITE CODE LIKE THIS.
Even if it were valid C I'd still reject it in a code review effort.

It's valid C.  You're dismissing the questions.  You're right about
avoidance of typing code like this.  Great; a good reminder.

It's valid in that it compiles, but not so in that it has defined
behaviour. arr[arr[0]-1] could be arr[0] meaning the other
arr[arr[0]] references don't have a compile time defined behaviour
[hence it's UB].

Furthermore, even if it were valid [which I contend it's not] it's
horrible and should not in any form be encouraged.

Tom
 
S

Shao Miller

Please don't fall into the trap of thinking that informative elements
are any less valuable than normative elements or can be ignored.  We put
them in because we believe them to be true (but redundant).  If they're
not, that's indicative of a defect in the normative part of the standard.
That trap is certainly avoidable. I fully believe that the
informative portions, including footnotes, do a fine job of taking
what's normative and offering something as a useful tip or consequence
of other detail. Thank you, as always.
 
S

Shao Miller

The only intention is to provide a standard hook (along with a big
loophole) to allow an implementation to do "the right thing" -- whatever
that is in its environment -- to support memory mapped I/O, shared
memory, or whatever.
Ok, that's great. Thus there's no fancy business about non-
simultaneous accesses implied; it's really for the implementation and
the programmers who know what it means for them. Precisely the answer
to some of my queries. Thanks yet again!
 
W

Walter Banks

Shao said:
Thank you for your kind response. In such a case where the '--'
performs its modification access right away, could it possibly collide
with a scheduled read of the same object, or would you schedule all
'volatile' accesses one-at-a-time, to avoid such a possible collision?

In our specific case most of our customers are doing embedded
applications and we essentially treat all writes as volatile.
The logic behind this is if someone wrote code that executed
multiple writes without intervening reads they intended to do
so and we don't eliminate the earlier writes.

We support several processors where it is possible to have a
write collision. In these we consistently resolve the write
conflict with rules during code generation using your terminology
we use software to avoid a collision. Write / read
conflicts are a little more complex resolve. We detect during
code generation and then use a handful of rules to resolve how
they should be handled.

I have been part of several processor instruction set designs
and generally side with utilizing machine generated code to
resolve code generation ambiguities rather than have a processor
try to resolve these issues. Almost without exception as experience
with the instruction set matures, ways are found to profitably
use a simpler less protective processor more effectively.




Regards,


Walter..
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top