Re: volatile and sequence points in (GASP) multithreaded programs
On Jul 2, 1:03 am, Dave Rahardja
<drahardja_atsign_pobox_dot_...@pobox.com> wrote:
I'm trying to understand when exactly the compiler is allowed to elide ac=
cess
to a variable.
Here is a common mistake made by a program waiting for a value
that is modified by an external entity:
bool flag;
while (!flag)
{
// Idle
}
In this case, the compiler is free to elide the access to
flag, and replace the while loop with an infinite loop.
Turning flag into a volatile variable, however, turns all
accesses to flag into observable behavior (=A71.9-6), and
prevents the compiler from eliding access to the variable.
However, I'm confused about the relationship between
observable behavior and sequence points (=A71.9-7). What does it
mean that all side effects before a sequence point is
completed before the next evaluation is performed?
Just what it says. But without some additional guarantees, the
above can still be an infinite loop. The standard doesn't
define what constitutes and "access", but rather leaves it
implementation defined. And at least on the most common
systems, the compilers I'm familiar with don't define access in
a way that will ensure that a thread sees modifications made to
the variable in another thread.
Of course, in your example, there is no observable behavior, so
the compiler can pretty much do whatever it wants.
Does it
mean that inserting a function call such as:
void foo(); // Defined in another compilation unit
bool flag;
while (!flag)
{
foo();
}
Forces the compiler to re-read flag at the head of each loop
iteration?
No.
My thought is that the compiler is /technically/ still allowed
to elide the access to flag, because its behavior is still not
observable. However, for practical purposes, a compiler will
not know what side effect foo() may have (including modifying
flag), so unless it performs whole-program optimization, it
cannot elide access to flag.
Typically, the compiler will generate different code depending
on the level of optimization, yes. That's about all you can say
here.
This line of questioning comes from my experiments with
multithreading, which is not defined by the standard. I'm
trying to determine exactly when I need to use a volatile
keyword, say when accessing data members in one object via
multiple threads.
Volatile has no defined meaning in a multi-threaded environment.
At least with the compilers I use (Sun CC, g++ and VC++), you
can forget it. It makes no difference with regards to the
guarantees you have. (That's not quite true; the guarantees are
adequate for code running on a single core machine. But do any
of those still exist?)
Even after synchronizing access to member variables (another
off-topic operation), I'm still not sure if my reads and
writes to member variables are liable to get optimized away.
However, =A71.9-7 seems to imply that modifying an object
defines a sequence point, and in all likelihood a compiler
will never optimize such operations away.
The end of a full expression is a sequence point. Accessing a
volatile variable is observable behavior.
Can someone point me to a reliable guide on how to correctly manage
optimizations for multithreaded programs?
Your compiler and OS documentation. If the compiler is Posix
conformant, the Posix standards. (I don't know where there is a
similar specification for Windows, but one probably exists
somewhere.)
Or do I simply have to declare all variables accessed by
multiple threads as volatile?
As I said above, volatile has no meaning in a multithreaded
environment. Posix does define a set of rules for C, which, by
extention, I would expect a Posix compliant C++ compiler to
respect. Presumably, Windows does the same---maybe they even
define C++ directly, I don't know. The standards committee is
currently working on a set of rules for C++, largely based on
the Posix rules (I think). In no case do you *have* to declare
anything volatile. You do have to obey other synchronization
rules, however---Posix defines a small set of functions which
ensure synchronization, and if any thread modifies an object,
the behavior is undefined unless all accesses in all threads are
protected by these functions. Anything else probably requires
inline assembler.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34