Clarification on the applicability of compile-time optimization to astruct variable which encompasse

J

Joshua Maurice

Um, what is it that compiler writers and thread library writers
understand volatile to mean?
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html


You seem to be making too many assumptions about my code.

I think it's pretty clear from what you've written else-thread.
How do you know, a priori, that my code is broken?  Do I have to
change my passwords again?

How do I know it's broken? By your own admission, you're using
volatile to try and affect inter-thread visibility (absent constructs
that actually give inter-thread visibility). By your previous
statements about using the C standard to derive useful threading
semantics for volatile, I doubt you've looked at the compiler
documentation and hardware documentation for your implementation. Thus
you have broken code. (I don't think you are trying to nit on "it
could be right by coincidence".)
 
J

Joshua Maurice

Well you know what?  You're going to have to wait until version 1.0
before you get to decide how wrong it is.

Let me share with you a story. I currently work for a company selling
Enterprise software. The software is heavily intertwined with
databases. We have a license for a large piece of code that does heavy
duty sorting. We have a contract with the guys who wrote this sorting
library for support and maintenance. One of our customers upgraded
some of their hardware, ran some tests - or possibly worse ran actual
usage - and noticed that one row out of a few billion was "missing".
Our engine dropped it, somewhere. It should be in the output, but it
wasn't. Of course, this is very bad for our company. This could cost
millions of dollars in sales. So, we get the guys who wrote the
sorting library, and immediately fly them out to us. We get them to
work on the code. I was only tangentially related to this process. It
took a week or so of work to find and fix the issue. What was the
problem? The guys who wrote the sorting library used "volatile", and
the customer just upgraded to some itanium processor or something. A
quick fix/hack later, and our customer was back in business, our
millions of dollars was secure, as was our company image and
reputation.

Use volatile at your own risk. I don't plan on using your software if
I can help it.
 
I

Ian Collins

Doing so would incur a performance penalty in comparison to my
synchronization primitives in some circumstances.

In other words, correctness would incur a performance penalty in
comparison to your synchronization primitives in some circumstances.
Look. In threaded applications, the truth associated with the value
associated with certain variables can be somewhat indeterminate. If
that's true in your code, you might want to be aware of it.

If I could parse that, I'd respond to it.
You aren't listening to me.

You aren't making much sense.

An example of you your synchronization primitives would clear things up.
 
T

Tim Rentsch

Uncle Steve said:
What is it about the volatile attribute that makes it
insufficient in your mind to the problem presented by
threaded code?

Let's take a specific example:

volatile int a = 1, b = 2;

... start up threads X and Y ...

... in parallel ... {

... in thread X ... {
a = 3;
b = 4;
}

... in thread Y ... {
printf( "a = %d\n", a );
printf( "b = %d\n", b );
}

}

Question: what values for a and b might thread Y print?

Answer: Besides the obvious possibilities of {1,2}, {3,2},
and {3,4}, the Standard allows a conforming implementation
to define volatile access in such a way that thread Y might
print 1 for a but 4 for b. In other words, using volatile
is not enough to guarantee write ordering. Moreover that
lack of guarantee holds true even if write-barrier opcodes
are used before and after each volatile-qualified access.

So it's kind of unfortunate but true - the meaning of volatile
is inherently non-portable. You may be able to write code
using volatile to do inter-thread communication that works
reliably on a number of implementations, but that depends
on decisions made by those implementations, and not just on
the Standard.

I don't have a copy of the standard, [...]

Easily solved. For C99:

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

For C11:

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
 
U

Uncle Steve

Let's take a specific example:

volatile int a = 1, b = 2;

... start up threads X and Y ...

... in parallel ... {

... in thread X ... {
a = 3;
b = 4;
}

... in thread Y ... {
printf( "a = %d\n", a );
printf( "b = %d\n", b );
}

}

Question: what values for a and b might thread Y print?

Answer: Besides the obvious possibilities of {1,2}, {3,2},
and {3,4}, the Standard allows a conforming implementation
to define volatile access in such a way that thread Y might
print 1 for a but 4 for b. In other words, using volatile
is not enough to guarantee write ordering. Moreover that
lack of guarantee holds true even if write-barrier opcodes
are used before and after each volatile-qualified access.

So it's kind of unfortunate but true - the meaning of volatile
is inherently non-portable. You may be able to write code
using volatile to do inter-thread communication that works
reliably on a number of implementations, but that depends
on decisions made by those implementations, and not just on
the Standard.

I never said I was relying on the volatile attribute exclusively to
provide coherence between threads. The bug up my butt concerning the
volatile keyword is more related to presenting consistent results in
d ata structures that might transition several states quickly, and I
don't want the presentation of those states to be optimized out
simply because the compiler decides it can.

I showed the potential for this in the simple timing code I posted
earlier. Note that removing the volatile qualifier from variables
that have them changes the output. So, my interest in the volatile
attribute is not related to thread synchronization so much as it is in
general data coherence in a complex threaded application.

Note that I have not yet found out whether structure attributes
propogate to substructures that are structure elements.

Thanks, I'll have a look.

I've been reviewing the code I posted previously, and I can no longer
make it malfunction the way it was breaking. I forget what I changed,
so I'm going to have to check into it before I can better illustrate
the phenomenon.



Regards,

Uncle Steve
 
U

Uncle Steve

[...]
Although the latest "C11" Standard adds some multi-threading
support, [...]

[...]I suggest you visit comp.programming.threads,
a newsgroup devoted to multi-threading issues. There you will learn
that `volatile' is neither necessary *nor* sufficient in connection
with variables shared between threads. [...]

Is there are C standard threading framework?

See above.
Threaded code is a de-facto reality in today's multi-core environment.
Save for the embedded sector, most systems will run threads in
parallel, and that is that. C has to recognize this watershed moment
in the refinement of the language.

See above.
What is it about the volatile attribute that makes it
insufficient in your mind to the problem presented by threaded code?

"Then Kolokolo Bird said, with a mournful cry, 'Go to the banks of
the great grey-green, greasy Limpopo River -- er, that is, the G,G-G,G
comp.programming.threads newsgroup -- all set about with fever-trees,
and find out.'"

By "go," I mean "go away," as in "Don't continue this thread in
comp.lang.c unless you specifically wish to discuss the threading
features of C11." Follow-ups set.

Your message excells as an example of myopic religious orthodoxy. I'm
not actually suggesting that there should be changes to the standard,
so I don't quite understand why you seem so threatened.


Regards,

Uncle Steve
 
U

Uncle Steve

On 7/6/2012 2:33 PM, Uncle Steve wrote:
]
Although the latest "C11" Standard adds some multi-threading
support, [...]

[...]I suggest you visit comp.programming.threads,
a newsgroup devoted to multi-threading issues. There you will learn
that `volatile' is neither necessary *nor* sufficient in connection
with variables shared between threads. [...]

Is there are C standard threading framework?

See above.

Threaded code is a de-facto reality in today's multi-core environment.
Save for the embedded sector, most systems will run threads in
parallel, and that is that. C has to recognize this watershed moment
in the refinement of the language.

See above.

What is it about the volatile attribute that makes it
insufficient in your mind to the problem presented by threaded code?

"Then Kolokolo Bird said, with a mournful cry, 'Go to the banks of
the great grey-green, greasy Limpopo River -- er, that is, the G,G-G,G
comp.programming.threads newsgroup -- all set about with fever-trees,
and find out.'"

By "go," I mean "go away," as in "Don't continue this thread in
comp.lang.c unless you specifically wish to discuss the threading
features of C11." Follow-ups set.

Your message excells as an example of myopic religious orthodoxy. I'm
not actually suggesting that there should be changes to the standard,
so I don't quite understand why you seem so threatened.

When you ask (somewhat incoherently) "Is there are C standard
threading framework," which part of

THE LATEST "C11" STANDARD ADDS SOME MULTI-THREADING SUPPORT

are you having trouble understanding?

C11's threading support is on-topic here in comp.lang.c -- but
you've indicated that you're not interested in C11 threads and in fact
are using Pthreads instead. Pthreads is off-topic here, so please help
reduce the noise by removing the discussion to comp.programming.threads
which "excells" [sic] on that topic.

It was previously mentioned that C11 threads were copied from
pthreads, although presumably there would be some difference in
semantics. Please don't cut off my hands if I inadvertently say
something against the standard, eh? I approve of standards, and I'm
not just saying that to get on your good side.


Regards,

Uncle Steve
 
U

Uncle Steve

Am 07.07.2012 01:58, schrieb Joshua Maurice:


No, this is misleading. volatile alone is not sufficient to warrant data
consistency between threads, but is necessary. volatile and _Atomic are
complementary specifications that *both* are needed if you want to make
sure that a thread always uses the latest stored value of a variable. If
you have

static int _Atomic a = ATOMIC_VAR_INIT(23);
if (tester)
for (;;) printf("current value is %d\n", a);

The compiler is allowed to change the loop into something equivalent to

{
int tmp = a; // do an atomic_load operation here
for (;;) printf("current value is %d\n", tmp);
}

and thus always print the same value.

static int _Atomic volatile a = ATOMIC_VAR_INIT(23);

would assure that each execution of printf would see an updated value.


C11 mutexes (as are POSIX mutexes) are much too heavy machinery where a
simple atomic would suffice. I think that atomics and the precise data
consistency model are the real novelty in C11 (and C++1). That is to

That was my thinking. The POSIX mutex api is really kind of awkward,
and on Linux the underlying implementation is more complicated than it
has to be. Currently I'm developing on x86_64 and x86, and I chose to
use "lock incl %0" and then inspect the result to detect collisions.
It is obviously very fast, and I can control how long a thread will
spin on the lock before sleeping. I have yet to investigate whether I
need to use a different instruction for the 64-bit arch.

A compiler supported atomic type would allow the removal of the
minimal inline asm that I'm using, but I can keep it to preserve some
backward compatibility with older systems.
provide a possibility of communicating safely between different threads
(and with signal handlers) without relying on OS support (or almost).

One potential application for non-mutex locking structures occurs in
some virtualized environments. Under the Xen hypervisor, it is
possible to share virtual memory segments between guest domains. The
facility is called 'grant pages', and looks a little like a SYSV
shared memory segment to the application. With such a memory window,
a ring buffer (or equivalent algorithm) can allow inter-domain
communications without any system calls. The same goes, of course,
for a ring-buffer instantiated in SYSV shared memory, but that is
limited to communications between processes on the same host.
Compared to TCP/IP IPC, this should greatly improve the available IPC
bandwidth among virtualized hosts on a multi-core platform.

I haven't yet got to the point of integrating support for Xen in any
of my code, but it's on my todo list.


Regards,

Uncle Steve
 
E

Eric Sosman

[...]
Although the latest "C11" Standard adds some multi-threading
support, [...]

[...]I suggest you visit comp.programming.threads,
a newsgroup devoted to multi-threading issues. There you will learn
that `volatile' is neither necessary *nor* sufficient in connection
with variables shared between threads. [...]

Is there are C standard threading framework?

See above.
Threaded code is a de-facto reality in today's multi-core environment.
Save for the embedded sector, most systems will run threads in
parallel, and that is that. C has to recognize this watershed moment
in the refinement of the language.

See above.
What is it about the volatile attribute that makes it
insufficient in your mind to the problem presented by threaded code?

"Then Kolokolo Bird said, with a mournful cry, 'Go to the banks of
the great grey-green, greasy Limpopo River -- er, that is, the G,G-G,G
comp.programming.threads newsgroup -- all set about with fever-trees,
and find out.'"

By "go," I mean "go away," as in "Don't continue this thread in
comp.lang.c unless you specifically wish to discuss the threading
features of C11." Follow-ups set.
 
E

Eric Sosman

]
Although the latest "C11" Standard adds some multi-threading
support, [...]

[...]I suggest you visit comp.programming.threads,
a newsgroup devoted to multi-threading issues. There you will learn
that `volatile' is neither necessary *nor* sufficient in connection
with variables shared between threads. [...]

Is there are C standard threading framework?

See above.
Threaded code is a de-facto reality in today's multi-core environment.
Save for the embedded sector, most systems will run threads in
parallel, and that is that. C has to recognize this watershed moment
in the refinement of the language.

See above.
What is it about the volatile attribute that makes it
insufficient in your mind to the problem presented by threaded code?

"Then Kolokolo Bird said, with a mournful cry, 'Go to the banks of
the great grey-green, greasy Limpopo River -- er, that is, the G,G-G,G
comp.programming.threads newsgroup -- all set about with fever-trees,
and find out.'"

By "go," I mean "go away," as in "Don't continue this thread in
comp.lang.c unless you specifically wish to discuss the threading
features of C11." Follow-ups set.

Your message excells as an example of myopic religious orthodoxy. I'm
not actually suggesting that there should be changes to the standard,
so I don't quite understand why you seem so threatened.

When you ask (somewhat incoherently) "Is there are C standard
threading framework," which part of

THE LATEST "C11" STANDARD ADDS SOME MULTI-THREADING SUPPORT

are you having trouble understanding?

C11's threading support is on-topic here in comp.lang.c -- but
you've indicated that you're not interested in C11 threads and in fact
are using Pthreads instead. Pthreads is off-topic here, so please help
reduce the noise by removing the discussion to comp.programming.threads
which "excells" [sic] on that topic.
 
J

Jens Gustedt

Am 07.07.2012 01:58, schrieb Joshua Maurice:
To be topical, here's the answer for C11: volatile is useless as a
portable threading construct. Do not use it. It's (maybe) useful in a
portable way only for 3 obscure situations: MMIO, setjmp and longjmp
stuff, and signal handling.

No, this is misleading. volatile alone is not sufficient to warrant data
consistency between threads, but is necessary. volatile and _Atomic are
complementary specifications that *both* are needed if you want to make
sure that a thread always uses the latest stored value of a variable. If
you have

static int _Atomic a = ATOMIC_VAR_INIT(23);
if (tester)
for (;;) printf("current value is %d\n", a);

The compiler is allowed to change the loop into something equivalent to

{
int tmp = a; // do an atomic_load operation here
for (;;) printf("current value is %d\n", tmp);
}

and thus always print the same value.

static int _Atomic volatile a = ATOMIC_VAR_INIT(23);

would assure that each execution of printf would see an updated value.
If you properly use C11 mutexes, there is
absolutely no need for volatile.

C11 mutexes (as are POSIX mutexes) are much too heavy machinery where a
simple atomic would suffice. I think that atomics and the precise data
consistency model are the real novelty in C11 (and C++1). That is to
provide a possibility of communicating safely between different threads
(and with signal handlers) without relying on OS support (or almost).


Jens
 
P

Phil Carmody

Uncle Steve said:
You aren't listening to me.

PKB.

I think you'll find that it's not Ian who needs the assistance.

Phil
--
I'd argue that there is much evidence for the existence of a God.
Pics or it didn't happen.
-- Tom (/. uid 822)
 
P

Phil Carmody

Tim Rentsch said:
Let's take a specific example:

volatile int a = 1, b = 2;

... start up threads X and Y ...

... in parallel ... {

... in thread X ... {
a = 3;
b = 4;
}

... in thread Y ... {
printf( "a = %d\n", a );
printf( "b = %d\n", b );
}

}

Question: what values for a and b might thread Y print?

Answer: Besides the obvious possibilities of {1,2}, {3,2},
and {3,4}, the Standard allows a conforming implementation
to define volatile access in such a way that thread Y might
print 1 for a but 4 for b. In other words, using volatile
is not enough to guarantee write ordering.

The two reads and two writes can be sequenced 6 ways.

ra rb wa wb - {1,2}
ra wa rb wb - {1,2}
ra wa wb rb - {1,4}
wa ra rb wb - {3,2}
wa ra wb rb - {3,4}
wa wb ra rb - {3,4}

{1.4} is not a violation of write ordering - wa is still
before wb.

Changing the order of the two printfs in thread Y would yield

rb ra wa wb - {2,1}
rb wa ra wb - {2,3}
rb wa wb ra - {2,3}
wa rb ra wb - {2,3}
wa rb wb ra - {2,3}
wa wb rb ra - {4,3}

Here the reversed output {4,1} is impossible, as it violates
the write ordering in thread X. (And in fact this read-order
being the reverse of write order is a common technique for
reducing the number of possibilities if you don't wish to
turn both the block read and the block writes into atomic
transactions.)

Phil
--
I'd argue that there is much evidence for the existence of a God.
Pics or it didn't happen.
-- Tom (/. uid 822)
 
T

Tim Rentsch

Phil Carmody said:
The two reads and two writes can be sequenced 6 ways.

ra rb wa wb - {1,2}
ra wa rb wb - {1,2}
ra wa wb rb - {1,4}
wa ra rb wb - {3,2}
wa ra wb rb - {3,4}
wa wb ra rb - {3,4}

{1.4} is not a violation of write ordering - wa is still
before wb.

Changing the order of the two printfs in thread Y would yield

rb ra wa wb - {2,1}
rb wa ra wb - {2,3}
rb wa wb ra - {2,3}
wa rb ra wb - {2,3}
wa rb wb ra - {2,3}
wa wb rb ra - {4,3}

Here the reversed output {4,1} is impossible, as it violates
the write ordering in thread X. (And in fact this read-order
being the reverse of write order is a common technique for
reducing the number of possibilities if you don't wish to
turn both the block read and the block writes into atomic
transactions.)

Good analysis. Clearly a weakness in my example;
thank you for pointing this out.

However, even if the printf()s in thread Y are done in
the other order, the reversed output {4,1} may occur in
a conforming implementation. This result holds because
of the great latitude granted by 6.7.3 p7, which allows
implementations to define what it means to access a
volatile object. That freedom means that how the
C program behaves could be different than what would
happen if, for example, an equivalent assembly language
program were run directly on the native hardware.
 
T

Tim Rentsch

Gareth Owen said:
Would it be correct to say that if both a & b are volatile, then the
compiler may not reorder them, but the processor may (in the absence of
an explicit memory barrier)?

It's more complicated than that. Certainly a write-buffer may
change the order of the writes (as one example), but it's also
true that "memory access" as far as a particular implementation is
concerned might not be the same as native machine instructions.
For a typical implementation, it's probably true that there will
be native machine instructions for every volatile access, and in
the same order as the abstract semantics would imply, but the
Standard allows enough latitude that implementations don't have to
do that (if they don't want to).
In which case, volatile may be (non-portably) sufficient for i386
uni-processors, say.

Again, it's likely that this will be true for most such
implementations, but the Standard doesn't force it to be
true if an implementation is determined to behave otherwise
(please excuse the anthropomorphism).
 
M

Myth__Buster

I don't think so.

Merely accessing a volatile object,
is considered to be a side effect.

Okay, thanks for the clarification.

Cheers,
Raghavan Santhanam
 
M

Myth__Buster

Myth__Buster said:
#include <stdio.h>

struct InterruptData
{
int numberOfInterrupts;
volatile int timer;
int numberOfTimesInterruptsMasked;
} InterruptMonitor;


/*
* Interrupt handler code.
*/

int main(void)
{
InterruptMonitor = InterruptMonitor;
return 0;
}

-------------

InterruptMonitor = InterruptMonitor; - Should this be a candidate for
compile-time optimization in spite of having a field qualified as
volatile?

I fear that your example may be one "a bit like" the code you really
care about, but I have to talk about what you post...

To a very large extent, the C standard washes it hands of volatile
objects. Sure, it says a fair bit about what *may* happen but it also
says "[w]hat constitutes an access to an object that has volatile-
qualified type is implementation-defined" (6.7.3 p7) and "[a]n actual
implementation need not evaluate part of an expression if it can deduce
that [...] no needed side effects are produced (including any caused by
[...] accessing a volatile object)" (5.1.2.3 p4).

Together, these give an implementation so much leeway that you often
need to refer to the implementation, not the standard. For example, an
implementation may well be able to deduce that the assignment is not a
"needed side effect", coming as it does at the start of main. Equally,
an implementation may treat all accesses to volatile-qualified objects
with kid gloves.


Okay, thanks for the clarification.

Cheers,
Raghavan Santhanam
 
U

Uncle Steve

Nobody is cutting off your hands. It's being suggested that,
unless you specifically want to discuss C11's threading support,
this discussion would be more appropriate in a different newsgroup.
Please don't overreact.

Well, I hope not since voice recognition isn't as efficient as typing.
I realize that c.l.c. is for discussions about the C language, without
necessary reference to any particular implementation. Nevertheless,
with threads supported by C11 I would imagine some discussion might be
allowed.


Regards,

Uncle Steve
 
K

Keith Thompson

Uncle Steve said:
It was previously mentioned that C11 threads were copied from
pthreads, although presumably there would be some difference in
semantics. Please don't cut off my hands if I inadvertently say
something against the standard, eh? I approve of standards, and I'm
not just saying that to get on your good side.

Nobody is cutting off your hands. It's being suggested that,
unless you specifically want to discuss C11's threading support,
this discussion would be more appropriate in a different newsgroup.
Please don't overreact.
 
J

Joshua Maurice

Would it be correct to say that if both a & b are volatile, then the
compiler may not reorder them, but the processor may (in the absence of
an explicit memory barrier)?

In which case, volatile may be (non-portably) sufficient for i386
uni-processors, say.

Perhaps. You'd have to check the hardware docs for the i386 and the
compiler docs to see how it treats volatile. I know some parts of my
company do exactly hacks like this. They wrote some message passing
software which has some of the highest throughput rates of any message
passing software in existence, and they did it by using volatile,
using specific guarantees of the x86, using specific implicit or
explicit guarantees of the compiler, and using some inline assembly. I
actually had the opportunity to hear a technical presentation by them
for my team, and I specifically asked what they do for other
platforms. The answer is that they only support the x86 architecture
(give or take - I'm not the expert), and that's apparently not a
problem as their customers are "fine" with that.
 

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

Forum statistics

Threads
474,079
Messages
2,570,573
Members
47,205
Latest member
ElwoodDurh

Latest Threads

Top