C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

volatile, atomic variables & multithreading
Goto page 1, 2  Next
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Graeme Prentice
Guest





PostPosted: Sat Feb 14, 2004 12:32 pm    Post subject: volatile, atomic variables & multithreading Reply with quote





What guarantees about the behaviour of a volatile type are there in C++?

I need to access a variable (e.g. pointer or int) in one thread (or
interrupt routine) and write to it in another thread (or interrupt
routine) without using a mutex or context switch (on some platforms this
requires saving/disabling/restoring interrupt state). The class
an_atomic_int below will provide the atomic access and can change
between platforms but all other code that uses it should work correctly
without being changed.

Does the fact that k is a volatile type guarantee that in function f1()
below, that at line **1, the get() function is called repeatedly and not
once only, and that after the statement in line **1 is complete, there
will be no further access to the k object i.e. that in line **2, the
three accesses of j1 will all use the same value?
( f1() is just for illustration - in practice it would have a non-busy
wait).
The class an_atomic_int has no operator int() function, but I might have
a second version that does have one.

Is it common practice in a Win32 X86 multithreading environment, to
assume that accessing an int or pointer is atomic and can be read or
written from multiple threads without synchronisation (since X86 32 bit
data access is known to be an atomic operation on Pentium 2 or better
for both aligned and non aligned data ??). This would presumably be
unreliable in a situation where the compiler might "secretly" access the
variable twice e.g. a*b might appear to access "a" once only but it
could be accessed multiple times (e.g. to check for negativity
separately from the value).

Alternatively, is it common practice in a Win32 multithreading
environment to synchronise access to ints and pointers when they're
accessed from more than one thread (read from one, write from another)?


class an_atomic_int
{
volatile int v;
public:
volatile an_atomic_int & operator =(const int & x) volatile
{
// platform dependent atomicity required here
// e.g. asm( "mov.l x,v" );
v = x;
return *this;
}
volatile an_atomic_int & operator =
(const volatile an_atomic_int & x) volatile
{
v = x.v;
return *this;
}
int get() const volatile
{
int x;
//asm( "mov.l v,x" );
x = v;
return x;
}
an_atomic_int (const int & x )
{
//asm( "mov.l x,v" );
v = x;
}
an_atomic_int(){}
};

typedef volatile an_atomic_int atomic_int;

atomic_int k(5);

// executed by multiple threads
int f1()
{
int j1;
do { j1 = k.get(); } while (j1==0); // **1

return j1 * j1 + 2345 / j1; // **2
}

int main()
{
k = 0;
for(;Wink
f1();
}


Graeme

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Ben Hutchings
Guest





PostPosted: Sun Feb 15, 2004 12:04 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote



Graeme Prentice wrote:
Quote:
What guarantees about the behaviour of a volatile type are there in
C++?

Very few.

Quote:
I need to access a variable (e.g. pointer or int) in one thread (or
interrupt routine) and write to it in another thread (or interrupt
routine) without using a mutex or context switch (on some platforms this
requires saving/disabling/restoring interrupt state).

Threads and interrupts are unfortunately both outside the scope of the
C++ standard. The guarantee of atomicity on volatile data only
applies to object of type sig_atomic_t accessed in signal handlers
running in the same context as the rest of the program. Access to
volatile-qualified objects is generally not compiled to operations
that are atomic and non-reorderable with respect to other threads.

In an interrupt handler you will have to use platform-specific
primitives. At a very low level there are memory barriers to control
the order of memory access and on some processors there are locked
operations. A slightly higher level construct is the spin-lock, which
can almost substitute for a mutex but will never sleep. However, to
avoid deadlock, you have to disable the interrupt when obtaining the
spin-lock in non-interrupt code. This is hairy stuff.

Where you don't have to share data with interrupt handlers, you can
still use those but you will generally do best to stick to mutexes.
Typical mutex implementations will spin for a while before sleeping on
SMP machines.

Quote:
The class an_atomic_int below will provide the atomic access and can
change between platforms but all other code that uses it should work
correctly without being changed.

Where you have comments with assembly-language code, do you mean that
something along those lines should be substituted in place of the
following line, or that you expect the compiler to generate that?

Quote:
Does the fact that k is a volatile type guarantee that in function f1()
below, that at line **1, the get() function is called repeatedly and not
once only, and that after the statement in line **1 is complete, there
will be no further access to the k object i.e. that in line **2, the
three accesses of j1 will all use the same value?

volatile-qualification has no useful effect on class-types, but on
objects of volatile-qualified built-in types it does mean (roughly)
that the implementation is required to generate code that accesses
them in the order implied by the source code (so far as that is
specified by the standard). So the answer is no, the fact that k.v
has a volatile-qualified type guarantees that.

Quote:
( f1() is just for illustration - in practice it would have a non-busy
wait).
The class an_atomic_int has no operator int() function, but I might have
a second version that does have one.

Is it common practice in a Win32 X86 multithreading environment, to
assume that accessing an int or pointer is atomic and can be read or
written from multiple threads without synchronisation (since X86 32 bit
data access is known to be an atomic operation on Pentium 2 or better
for both aligned and non aligned data ??).

It is atomic, but that doesn't mean it can't be re-ordered with
respect to other atomic operations. To build larger atomic operations
you need memory barriers. On x86 I believe these are implied by the
lock-prefix on instructions.

Quote:
This would presumably be
unreliable in a situation where the compiler might "secretly" access the
variable twice e.g. a*b might appear to access "a" once only but it
could be accessed multiple times (e.g. to check for negativity
separately from the value).

Some compilers have intrinsic functions for memory barriers and will
avoid re-ordering across them. Any compiler that supports multi-
threading must avoid re-ordering access to static or heap objects
across calls into functions for which it can't "see" the source, which
will be the case for at least the critical parts of synchronisation
primitives.

Quote:
Alternatively, is it common practice in a Win32 multithreading
environment to synchronise access to ints and pointers when they're
accessed from more than one thread (read from one, write from another)?

I don't know whether it's common. It is generally the right thing
to do though.

<quoted code snipped -mod/fwg>
--
Ben Hutchings
The program is absolutely right; therefore, the computer must be wrong.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Jack Klein
Guest





PostPosted: Sun Feb 15, 2004 12:06 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote



On 14 Feb 2004 07:32:09 -0500, Graeme Prentice <invalid (AT) yahoo (DOT) co.nz>
wrote in comp.lang.c++.moderated:

Quote:
What guarantees about the behaviour of a volatile type are there in C++?

There are some issues with volatile in C++ that are the subjects of
defect reports that have not yet been dealt with.

Quote:
I need to access a variable (e.g. pointer or int) in one thread (or
interrupt routine) and write to it in another thread (or interrupt
routine) without using a mutex or context switch (on some platforms this
requires saving/disabling/restoring interrupt state). The class
an_atomic_int below will provide the atomic access and can change
between platforms but all other code that uses it should work correctly
without being changed.

Note that if you use the sig_atomic_t type defined in <signal.h> or
<csignal>, you will not need to change it between platforms. This is
the type specifically designed for this purpose in C++, as in C.

Quote:
Does the fact that k is a volatile type guarantee that in function f1()
below, that at line **1, the get() function is called repeatedly and not
once only, and that after the statement in line **1 is complete, there
will be no further access to the k object i.e. that in line **2, the
three accesses of j1 will all use the same value?

Yes to both questions.

Quote:
( f1() is just for illustration - in practice it would have a non-busy
wait).
The class an_atomic_int has no operator int() function, but I might have
a second version that does have one.

It might be better to have an operator sig_atomic_t() function.

Quote:
Is it common practice in a Win32 X86 multithreading environment, to
assume that accessing an int or pointer is atomic and can be read or
written from multiple threads without synchronisation (since X86 32 bit
data access is known to be an atomic operation on Pentium 2 or better
for both aligned and non aligned data ??). This would presumably be
unreliable in a situation where the compiler might "secretly" access the
variable twice e.g. a*b might appear to access "a" once only but it
could be accessed multiple times (e.g. to check for negativity
separately from the value).

The first part of this paragraph would be better directed to a
newsgroup like news:comp.lang.asm.x86, as the C++ standard does not
specify the hardware bus cycle operation of the Intel processors. The
second part is a matter of the language, and a compiler that generated
code to access a volatile more than once for a single read access in
the abstract machine would be non-conforming.

Note that under the current C++ standard, assigning to a volatile
object yields an lvalue result. An lvalue in an expression calls for
an lvalue-to-rvalue conversion in the abstract machine, and can
normally be optimized away if the rvalue is not used. Since accesses
to volatile objects are not supposed to be optimized away, a compiler
should generate code to read back the value after writing. This can
have serious consequences if the volatile object is actually a
memory-mapped hardware device. Not all compilers are conforming in
this respect, and the issue needs to be clarified in a future update
to the standard.

Quote:
Alternatively, is it common practice in a Win32 multithreading
environment to synchronise access to ints and pointers when they're
accessed from more than one thread (read from one, write from another)?

Questions about what it common practice in Win32 programs should be
directed to a Windows programming or Microsoft support group.

Quote:
class an_atomic_int
{
volatile int v;

Again, I suggest making use of the standard type sig_atomic_t here,
that is what it is defined for.

[snip]

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Graeme Prentice
Guest





PostPosted: Mon Feb 16, 2004 12:35 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

On 15 Feb 2004 07:06:36 -0500, Jack Klein wrote:

Quote:
On 14 Feb 2004 07:32:09 -0500, Graeme Prentice <invalid (AT) yahoo (DOT) co.nz
wrote in comp.lang.c++.moderated:

What guarantees about the behaviour of a volatile type are there in C++?

There are some issues with volatile in C++ that are the subjects of
defect reports that have not yet been dealt with.

I need to access a variable (e.g. pointer or int) in one thread (or
interrupt routine) and write to it in another thread (or interrupt
routine) without using a mutex or context switch (on some platforms this
requires saving/disabling/restoring interrupt state). The class
an_atomic_int below will provide the atomic access and can change
between platforms but all other code that uses it should work correctly
without being changed.

Note that if you use the sig_atomic_t type defined in csignal>, you will not need to change it between platforms. This is
the type specifically designed for this purpose in C++, as in C.


Unfortunately there's no guarantee as to whether it's signed or unsigned
or what its size is except that if it's signed it has to be at least
-127 to +127 or 0 to 255 if it's unsigned which means it is not platform
independent and useless for my purpose because I need a 32 bit unsigned
int on this occasion.


Quote:

Does the fact that k is a volatile type guarantee that in function f1()
below, that at line **1, the get() function is called repeatedly and not
once only, and that after the statement in line **1 is complete, there
will be no further access to the k object i.e. that in line **2, the
three accesses of j1 will all use the same value?

Yes to both questions.


So is this 1.9 para 7 that gives this guarantee.
<quote>
Accessing an object designated by a volatile lvalue (3.10), modifying an
object, ... [snip] are all side effects. ... at sequence points, ...
all side effects of previous evaluations shall be complete
<end quote>

Does this mean the compiler is prohibited from accessing a volatile
lvalue beyond the next sequence point from where it is referenced by the
abstract machine? The Harbison & Steele C book I have suggests this
might not be the case (I'm not sure).


I realize that the meaning of "accessed" is implementation-defined but
for the compiler we're using (gcc) there is a detailed description of
what accessed means in this situation
http://gcc.gnu.org/onlinedocs/gcc-3.3.2/gcc/Volatiles.html#Volatiles

and Microsoft similarly on MSDN
http://makeashorterlink.com/?A60224867
http://makeashorterlink.com/?X17223867


Quote:

( f1() is just for illustration - in practice it would have a non-busy
wait).
The class an_atomic_int has no operator int() function, but I might have
a second version that does have one.

It might be better to have an operator sig_atomic_t() function.

Is it common practice in a Win32 X86 multithreading environment, to
assume that accessing an int or pointer is atomic and can be read or
written from multiple threads without synchronisation (since X86 32 bit
data access is known to be an atomic operation on Pentium 2 or better
for both aligned and non aligned data ??). This would presumably be
unreliable in a situation where the compiler might "secretly" access the
variable twice e.g. a*b might appear to access "a" once only but it
could be accessed multiple times (e.g. to check for negativity
separately from the value).

The first part of this paragraph would be better directed to a
newsgroup like news:comp.lang.asm.x86, as the C++ standard does not
specify the hardware bus cycle operation of the Intel processors. The
second part is a matter of the language, and a compiler that generated
code to access a volatile more than once for a single read access in
the abstract machine would be non-conforming.


I deliberately left the word "volatile" out of my question - I was
referring to non volatile variables. Can you quote what part of the
standard prohibits multiple reads for a volatile variable?


Quote:

Note that under the current C++ standard, assigning to a volatile
object yields an lvalue result. An lvalue in an expression calls for
an lvalue-to-rvalue conversion in the abstract machine, and can
normally be optimized away if the rvalue is not used. Since accesses
to volatile objects are not supposed to be optimized away, a compiler
should generate code to read back the value after writing. This can
have serious consequences if the volatile object is actually a
memory-mapped hardware device. Not all compilers are conforming in
this respect, and the issue needs to be clarified in a future update
to the standard.

Alternatively, is it common practice in a Win32 multithreading
environment to synchronise access to ints and pointers when they're
accessed from more than one thread (read from one, write from another)?

Questions about what it common practice in Win32 programs should be
directed to a Windows programming or Microsoft support group.


I was trying to make a point relevant to all C++ programs, using a
common platform that just happens to have an atomic int as an example,
that the fact that a data type is atomic doesn't guarantee that it's
safe to share between threads and that to get the safety, a mutex may be
needed, which has non trivial affect on performance and "throughput" of
a thread. However, if volatile provides the semantics you say, then in
some cases, a volatile atomic data type can be shared safely without a
mutex.

For the expression a*b where a is volatile and atomic, the compiler is
entitled to read "a" multiple times during the evaluation of a*b.
Similarly for cout << a, "a" can be read multiple times - the fact that
"a" is passed by value to operator<< probably makes this safe.
However, if I write x=a; y = x*b; then the evaluation of x*b should work
correctly if the compiler is prohibited from accessing "a" again after
the statement x=a;

If "a" is memory mapped IO, what prevents the compiler from reading the
IO twice when evaluating x=a; for volatile "a". If "a" is a byte, then
of course there's no reason to read it twice so the code is safe enough
but does the C or C++ language prohibit multiple reads - if so, where?

Graeme

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Graeme Prentice
Guest





PostPosted: Mon Feb 16, 2004 12:37 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

On 15 Feb 2004 07:04:06 -0500, Ben Hutchings wrote:

Quote:
Graeme Prentice wrote:
What guarantees about the behaviour of a volatile type are there in
C++?

Very few.

There should be some. I'd like to see the standard have a description
in plain English of what is guaranteed. Most people assume that
while(x) {} where x is volatile, means that x has to be re-read each
time. 1.9 para 7 defines "accessing a volatile variable" to be a side
effect that has to be complete by the next sequence point and that
subsequent side effects have yet to occur.

For while ( a=b) {} , for non volatile a and b, the assignment to a is
also a side effect - does this mean that the compiler is required to
carry out the side effect every time around the while loop? What
prevents an optimiser from determining the assignment only needs to be
done once? 1.9 para 7 doesn't say access to a volatile variable can't
be optimised away. The C standard goes further and says (in 5.1.2.3
para 3) that an expression has to be evaluated if it has side effects.
The C++ standard is more vague and says (in a 1.9 footnote)
<quote>
an actual implementation need not evaluate part of an expression if it
can deduce that its value is not used and that no side effects affecting
the observable behavior of the program are produced.
<>

What does this mean in relation to while(x) or while(a=b) above? If a
compiler can determine that repeating th eassignment every time makes no
difference, does it still have to do it?

Why doesn't C++ give the guarantee that C does in 5.1.2.3 para 3?


In 7.1.5.1 para 8 there is an unfortunate statement that <quote>
volatile is a hint to the implementation to avoid aggressive
optimization <end quote> which then disqualifies itself by saying that
1.9 has detailed semantics. What would be the difference between
aggressive and non-aggressive optimisation? This sentence in 7.1.5.1
seems thoughtless and loose - if volatile is just a hint, then 1.9
should say so.


[snip]

Quote:

In an interrupt handler you will have to use platform-specific
primitives. At a very low level there are memory barriers to control
the order of memory access and on some processors there are locked
operations. A slightly higher level construct is the spin-lock, which
can almost substitute for a mutex but will never sleep. However, to
avoid deadlock, you have to disable the interrupt when obtaining the
spin-lock in non-interrupt code. This is hairy stuff.

Where you don't have to share data with interrupt handlers, you can
still use those but you will generally do best to stick to mutexes.
Typical mutex implementations will spin for a while before sleeping on
SMP machines.

Fortunately I'm writing code for an embedded application and don't have
to worry about multiple processors. In a multiple processor environment
I wouldn't attempt to write "low level" software.


Quote:

The class an_atomic_int below will provide the atomic access and can
change between platforms but all other code that uses it should work
correctly without being changed.

Where you have comments with assembly-language code, do you mean that
something along those lines should be substituted in place of the
following line, or that you expect the compiler to generate that?

The first - it was supposed to be "pseudo code" that provided atomic
access. e.g. on the Hitachi micro we're using, a 32 bit "long"
instruction does an atomic read.


Quote:

Does the fact that k is a volatile type guarantee that in function f1()
below, that at line **1, the get() function is called repeatedly and not
once only, and that after the statement in line **1 is complete, there
will be no further access to the k object i.e. that in line **2, the
three accesses of j1 will all use the same value?

volatile-qualification has no useful effect on class-types,

Yes it does. It means that all members of an object of that type have
to be treated as volatile.


Quote:
but on
objects of volatile-qualified built-in types it does mean (roughly)
that the implementation is required to generate code that accesses
them in the order implied by the source code (so far as that is
specified by the standard). So the answer is no, the fact that k.v
has a volatile-qualified type guarantees that.

( f1() is just for illustration - in practice it would have a non-busy
wait).
The class an_atomic_int has no operator int() function, but I might have
a second version that does have one.

Is it common practice in a Win32 X86 multithreading environment, to
assume that accessing an int or pointer is atomic and can be read or
written from multiple threads without synchronisation (since X86 32 bit
data access is known to be an atomic operation on Pentium 2 or better
for both aligned and non aligned data ??).

It is atomic, but that doesn't mean it can't be re-ordered with
respect to other atomic operations. To build larger atomic operations
you need memory barriers. On x86 I believe these are implied by the
lock-prefix on instructions.

This would presumably be
unreliable in a situation where the compiler might "secretly" access the
variable twice e.g. a*b might appear to access "a" once only but it
could be accessed multiple times (e.g. to check for negativity
separately from the value).

Some compilers have intrinsic functions for memory barriers and will
avoid re-ordering across them. Any compiler that supports multi-
threading must avoid re-ordering access to static or heap objects
across calls into functions for which it can't "see" the source, which
will be the case for at least the critical parts of synchronisation
primitives.

I'm a little lost here. What do you mean by a "compiler that supports
multithreading". Do you happen to know how thread safety of heap memory
is generally managed - do all heap management functions use a mutex?
What do you mean by "re-ordering access" - do compilers have a
"multi-threaded" mode and "non multi-threaded" mode?

Graeme

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Tue Feb 17, 2004 11:18 am    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

Graeme Prentice <invalid (AT) yahoo (DOT) co.nz> wrote


Quote:
What guarantees about the behaviour of a volatile type are there in C++?

That the implementation must define the behavior -- the basic semantics
of volatile are implementation defined.

Quote:
I need to access a variable (e.g. pointer or int) in one thread (or
interrupt routine) and write to it in another thread (or interrupt
routine) without using a mutex or context switch (on some platforms
this requires saving/disabling/restoring interrupt state).

First, is it a question of threads or of interrupts. If it is a
question of threads, volatile is probably worthless for what you want;
it certainly isn't portable. If it is a question of interrupts, you
will have to find out what the platform implements; anything involving
interrupts is highly platform specific. But volatile does often play a
role here.

Quote:
The class an_atomic_int below will provide the atomic access and can
change between platforms but all other code that uses it should work
correctly without being changed.

Does the fact that k is a volatile type guarantee that in function
f1() below, that at line **1, the get() function is called repeatedly
and not once only, and that after the statement in line **1 is
complete, there will be no further access to the k object i.e. that in
line **2, the three accesses of j1 will all use the same value?

First, the volatile on the get function or the class object buy you
nothing at all here. The "observable behavior" of the code must be the
same as if the get function were called each time in the loop.
Accessing a volatile variable is part of the observable behavior, so the
member variable v must be accessed each time in the loop (whether get()
is called or not).

What constitutes an access, however, is implementation defined. So you
will have to be very, very careful here, and read the documentation of
your implementation very carefully. (FWIW: none of the implementations
I know take any particular steps to ensure data coherence between
multiple processors when accessing a volatile variable.)

Quote:
( f1() is just for illustration - in practice it would have a non-busy
wait). The class an_atomic_int has no operator int() function, but I
might have a second version that does have one.

Is it common practice in a Win32 X86 multithreading environment, to
assume that accessing an int or pointer is atomic and can be read or
written from multiple threads without synchronisation (since X86 32
bit data access is known to be an atomic operation on Pentium 2 or
better for both aligned and non aligned data ??).

Excuse me, but I don't think that pointer or int access is guaranteed to
be atomic on an Intel. On the other hand, if the int or the pointer are
declared in C or C++, they will be aligned; accessing an aligned int, or
an aligned near pointer, is guaranteed to be atomic. I don't think
accessing a far pointer is ever atomic, and presumably, if you are
handling hardware level interrupts, you are also dealing with far
pointers. (But attention: I've not worked on Intel architecture for
some years now, so take what I say with a grain of salt.)

With regards to multiple threads, you generally have to consider that
they can be executed in a truely parallel fashion, each thread on a
different processor, and that each processor has its own cache memory
and memory access pipelines. In general, you need special hardware
instructions (memory barriers) to guarantee coherence between
processors. From what I have been told, however, IA-32 guarantees the
coherence without special instructions. Just be aware that your code is
emminatly unportable (but you know that already, if you are dealing with
interrupts).

Quote:
This would presumably be unreliable in a situation where the compiler
might "secretly" access the variable twice e.g. a*b might appear to
access "a" once only but it could be accessed multiple times (e.g. to
check for negativity separately from the value).

The problem isn't only the compiler, it is the underlying hardware.

Quote:
Alternatively, is it common practice in a Win32 multithreading
environment to synchronise access to ints and pointers when they're
accessed from more than one thread (read from one, write from
another)?

Presumably. Given that code won't generally work otherwise. (It's
certainly the rule in Posix code, and I can't imagine doing differently
in Windows.)

Quote:
class an_atomic_int
{
volatile int v;
public:
volatile an_atomic_int & operator =(const int & x) volatile

The volatile at the class level here serve no purpose. You don't need
them.

[...]
Quote:
};

typedef volatile an_atomic_int atomic_int;

atomic_int k(5);

// executed by multiple threads
int f1()
{
int j1;
do { j1 = k.get(); } while (j1==0); // **1

If you are waiting for an interrupt on a dedicated machine, this might
be acceptable. If not...

I'm not too sure what context you are working in. On a normal machine,
with an OS, you cannot install an interrupt handler without special
privileges, and interrupt handlers must respect a number of special
rules. Normally, too, the system will provide some sort of mechanism
for you to do a non-busy wait on such an interrupt in your driver.

--
James Kanze GABI Software mailto:kanze (AT) gabi-soft (DOT) fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Francis Glassborow
Guest





PostPosted: Tue Feb 17, 2004 2:22 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

In message <blsv20p69i58ssmv36nn77kh77vrid65ef (AT) 4ax (DOT) com>, Graeme Prentice
<invalid (AT) yahoo (DOT) co.nz> writes
Quote:
For while ( a=b) {} , for non volatile a and b, the assignment to a is
also a side effect - does this mean that the compiler is required to
carry out the side effect every time around the while loop? What
prevents an optimiser from determining the assignment only needs to be
done once? 1.9 para 7 doesn't say access to a volatile variable can't
be optimised away. The C standard goes further and says (in 5.1.2.3
para 3) that an expression has to be evaluated if it has side effects.
The C++ standard is more vague and says (in a 1.9 footnote)
quote
an actual implementation need not evaluate part of an expression if it
can deduce that its value is not used and that no side effects affecting
the observable behavior of the program are produced.

How does it fail to say that?

Accessing an object designated by a volatile lvalue (3.10) ... are all
side effects, which are changes in the execution environment.

I think that makes it abundantly clear that the last line of the
footnote you quote does not give any licence to optimise away any access
(or call of a function that accesses) a volatile variable.


--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Jeff Greif
Guest





PostPosted: Tue Feb 17, 2004 2:32 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

"Graeme Prentice" <invalid (AT) yahoo (DOT) co.nz> wrote

Quote:
On 15 Feb 2004 07:04:06 -0500, Ben Hutchings wrote:

Graeme Prentice wrote:
What guarantees about the behaviour of a volatile type are there in
C++?

Very few.

There should be some. I'd like to see the standard have a description
in plain English of what is guaranteed. Most people assume that
while(x) {} where x is volatile, means that x has to be re-read each
time. 1.9 para 7 defines "accessing a volatile variable" to be a side
effect that has to be complete by the next sequence point and that
subsequent side effects have yet to occur.

Even if it were guaranteed it would not work in a multi-threaded program on

an SMP box. Even if the variable is "re-read", it might be read from cache,
but the cache of one processor is not guaranteed to be in sync with that of
another unless a 'memory barrier' has been encountered since the last change
on one processor. This is usually achieved using mutexes or critical
sections or something similar.

Jeff


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Graeme Prentice
Guest





PostPosted: Tue Feb 17, 2004 6:30 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

On 17 Feb 2004 06:18:20 -0500, [email]kanze (AT) gabi-soft (DOT) fr[/email] wrote:

Quote:
Graeme Prentice <invalid (AT) yahoo (DOT) co.nz> wrote in message
news:<m6sq20lhsvuf2khltd8t5a0jbvrguooit1 (AT) 4ax (DOT) com>...

What guarantees about the behaviour of a volatile type are there in C++?

That the implementation must define the behavior -- the basic semantics
of volatile are implementation defined.

Where does the C++ standard say that semantics of volatile are
implementaton defined?

Having read Jack and Ben's replies to me I finally realised that it must
be 1.9 para 6 that allowed Jack to answer Yes to both of the questions I
asked about my code.

<1.9 para 6 - quote>
The observable behavior of the abstract machine is its sequence of reads
and writes to volatile data and calls to library I/O functions.
<end quote>

This means that the compiler cannot do any additional accesses to
volatile variables than what is implied by the code and it has to do all
accesses to volatile variables as specified by the code, in the order
specified. Also, all code has to be executed in the order specified
unless it makes no difference to the observable behaviour i.e. that the
same values will be written to volatile variables and passed to library
IO functions. If this weren't the case then the compiler could execute
the following program in reverse and not violate the standard.

int main()
{
int a = 20;
cout << a;
}

The only thing that requires the compiler to assign value 20 to "a"
before calling operator<< is 1.9 para 6.

The same goes for the sequence "acquire lock" "perform operation"
"release lock". Acquire lock and release lock have to be written in
such a way that prevents the compiler from reordering things.


There seems to be nothing whatsoever "implementation defined" about 1.9
para 6. 7.1.5.1 para 8 says volatile is a hint to the compiler to
avoid aggressive optimisations and that the semantics of volatile are
intended to be the same in C++ as they are in C, yet 1.9 para 1 to 8
(when you understand them) leave no room to move on the semantics of
volatile, so why do you say it's implementation defined? What am I
missing?



Quote:

I need to access a variable (e.g. pointer or int) in one thread (or
interrupt routine) and write to it in another thread (or interrupt
routine) without using a mutex or context switch (on some platforms
this requires saving/disabling/restoring interrupt state).

First, is it a question of threads or of interrupts.

The immediate need is sharing between threads, however, it amounts to
the same thing on our system.

[snip]

Quote:

First, the volatile on the get function or the class object buy you
nothing at all here.

I wasn't sure what the rules regarding volatile were so I added volatile
to the class type to see what comments it attracted. I suspected it was
redundant for the code I posted but 3.9.3 para 3 indicates volatile
qualification on a class object makes all non-static ... members
volatile.

Quote:

What constitutes an access, however, is implementation defined. So you
will have to be very, very careful here, and read the documentation of
your implementation very carefully. (FWIW: none of the implementations
I know take any particular steps to ensure data coherence between
multiple processors when accessing a volatile variable.)

It's a single processor embedded application on a Hitachi micro using
GCC and GCC provides adequate documentation on what constitutes an
access to a volatile variable
http://gcc.gnu.org/onlinedocs/gcc-3.3.2/gcc/Volatiles.html#Volatiles


[snip]

Quote:
Is it common practice in a Win32 X86 multithreading environment, to
assume that accessing an int or pointer is atomic and can be read or
written from multiple threads without synchronisation (since X86 32
bit data access is known to be an atomic operation on Pentium 2 or
better for both aligned and non aligned data ??).

Excuse me, but I don't think that pointer or int access is guaranteed to
be atomic on an Intel.


I didn't say that pointer or int access was atomic - I said that 32 bit
data access was atomic on certain Intel CPUs. Since multithreaded apps
seem to be fairly common on Windows I was curious to know whether there
was a general awareness of what is atomic and what isn't.


Quote:
On the other hand, if the int or the pointer are
declared in C or C++, they will be aligned; accessing an aligned int, or
an aligned near pointer, is guaranteed to be atomic. I don't think
accessing a far pointer is ever atomic, and presumably, if you are
handling hardware level interrupts, you are also dealing with far
pointers. (But attention: I've not worked on Intel architecture for
some years now, so take what I say with a grain of salt.)


With regards to multiple threads, you generally have to consider that
they can be executed in a truely parallel fashion, each thread on a
different processor, and that each processor has its own cache memory
and memory access pipelines. In general, you need special hardware
instructions (memory barriers) to guarantee coherence between
processors. From what I have been told, however, IA-32 guarantees the
coherence without special instructions. Just be aware that your code is
emminatly unportable (but you know that already, if you are dealing with
interrupts).

I know nothing about multi processor systems but now that it's been
mentioned to me several times in this thread, I'm curious to know more
about it and whether Windows runs on multiprocessor hardware.


Quote:

I'm not too sure what context you are working in. On a normal machine,
with an OS, you cannot install an interrupt handler without special
privileges, and interrupt handlers must respect a number of special
rules. Normally, too, the system will provide some sort of mechanism
for you to do a non-busy wait on such an interrupt in your driver.


It's an embedded project and this is the first time we've used
preemptive multithreading in an embedded application. The code in
question is a circular char buffer handler where one thread (or
potentially an interrupt routine, but currently not) adds characters to
the buffer and a possibly different thread (or interrupt routine removes
them). This code here for example, returns the number of characters in
the buffer. insertOffset and removeOffset are volatile and atomic on
our current micro and when they're equal, the buffer is empty.

uint32 in, out;
for (;Wink
{
in = insertOffset;
out = removeOffset;
// if insertOffset has changed then re-read.
if ( in == insertOffset )
{ break;
}
}
if (in >= out)
return in - out;
return bufferLength - out + in;


I realise this code isn't portable and it will be reviewed after I
figure out what guarantee volatile gives. The generated code works
correctly on our present CPU. I could just disable interrupts before
reading the insert and remove values but I believe the code works
without doing that.

Graeme

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Ben Liddicott
Guest





PostPosted: Tue Feb 17, 2004 6:31 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

On Win32 you should use the InterlockedXXX functions. These internally use bus-locking instructions to ensure that other processors flush the
values from their caches. Have a look in the debugger if you are interested: The functions are very simple.

Note that this won't help you if other threads are caching the values of pointers in registers, or optimising away accesses. They may still be
using stale values.

If you are just creating a thread-safe integer class, I strongly suggest that on Win32 you use the Interlocked functions. You can create any
arithmetic operation using InterlockedCompareExchange, by reading the current value into a local variable, performing the operation, and saving
the result into a second variable, and then using InterlockedCompareExchange to change the value.

long AddLocked(long* plValue, long lToAdd){
while(true){
long lOld = InterlockedCompareExchange(plValue, 0, 0);
long lNew = lOld + lToAdd;
long lCompare = InterlockedCompareExchange(plValue, lNew, lOld);
if(lCompare == lOld){
return lNew;// may be out of date already.
}
}
}

If it's a pointer, James Kanze says that on Intel reading aligned pointers is guaranteed atomic. I am not sure if that meets requirements for
you, as depending on the semantics of the value, you may not be allowed to use the value anymore -- another thread may delete the referenced it
after you have read the pointer, for example.

Personally, I would be very wary or relying on the semantics of volatile for this purpose. My advice would be to use the InterlockedXXX
functions to read the value into a stack variable and use it from there. If you want to replace an object, you should prepare the new object,
referencing it from a local variable, then use InterlockedCompareExchange to replace the object.

Note that if your object has complex semantics, such that you need to control access to more than one memory location simultaneously (which is
the usual case, lets face it) you need to either set up a strict ownership scheme, probably with a reference counting scheme where threads are
given on startup their own local pointers (associated with an increment in the count), and the object controls its own lifetime; or you need to
control access to the object with a critical section.

If your object has a strict lifetime guarantee (for example, initialized before any secondary threads are started, and destroyed after they all
exit, and static in between) such methods are not necessary.


--
Cheers,
Ben Liddicott


"Graeme Prentice" <invalid (AT) yahoo (DOT) co.nz> wrote

Quote:
Is it common practice in a Win32 X86 multithreading environment, to
assume that accessing an int or pointer is atomic and can be read or
written from multiple threads without synchronisation (since X86 32 bit
data access is known to be an atomic operation on Pentium 2 or better
for both aligned and non aligned data ??). This would presumably be
unreliable in a situation where the compiler might "secretly" access the
variable twice e.g. a*b might appear to access "a" once only but it
could be accessed multiple times (e.g. to check for negativity
separately from the value).

Alternatively, is it common practice in a Win32 multithreading
environment to synchronise access to ints and pointers when they're
accessed from more than one thread (read from one, write from another)?


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Tue Feb 17, 2004 9:40 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

Graeme Prentice <invalid (AT) yahoo (DOT) co.nz> wrote

Quote:
On 15 Feb 2004 07:04:06 -0500, Ben Hutchings wrote:

Graeme Prentice wrote:
What guarantees about the behaviour of a volatile type are there
in C++?
Very few.

There should be some. I'd like to see the standard have a description
in plain English of what is guaranteed. Most people assume that
while(x) {} where x is volatile, means that x has to be re-read each
time. 1.9 para 7 defines "accessing a volatile variable" to be a side
effect that has to be complete by the next sequence point and that
subsequent side effects have yet to occur.

So propose some wording that would be relevant for all platforms. The
reason that there are so few guarantees is that no one to date has been
capable of doing that. So, while the standard defines "accessing a
volatile object" as "observable behavior", it leaves the definition of
what constitutes an access up to the implementation. I don't think that
there is any problem that in "while ( x ) {}", 'x' must be reread each
time through the loop. There is a question as to what "reread" means.
All of the compilers I know consider that just emitting a machine
instruction which "accesses" memory is sufficient; even though on modern
machines, this may not translate to any external cycles on the memory
bus.

Quote:
For while ( a=b) {} , for non volatile a and b, the assignment to a is
also a side effect - does this mean that the compiler is required to
carry out the side effect every time around the while loop?

All the compiler is required to do is respect the observable behavior.

Quote:
What prevents an optimiser from determining the assignment only needs
to be done once?

What's to prevent an optimizer from determining that the loop never
finishes, unless b is initially 0, and replacing the code by something
like:

if ( b != 0 ) while ( true ) {}
a = 0 ;

Nothing.

Quote:
1.9 para 7 doesn't say access to a volatile variable can't be
optimised away.

Paragraph 6 says: "The observable behavior of the abstract machine is
its sequence of reads and writes to volatile data and calls to library
I/O functions." Paragraph 11 says that "the least requirements on a
conforming implementation are: -- At sequence points, volatile objects
are stable in the sense that previous evaluations are complete and
subsequent evaluations have not yet occured." The wording could be
clearer, but I think that the intent is clear.

Quote:
The C standard goes further and says (in 5.1.2.3 para 3) that an
expression has to be evaluated if it has side effects. The C++
standard is more vague and says (in a 1.9 footnote)

quote
an actual implementation need not evaluate part of an expression if it
can deduce that its value is not used and that no side effects affecting
the observable behavior of the program are produced.


What does this mean in relation to while(x) or while(a=b) above?

That all that counts is the "observable behavior". If x is volatile,
above, then accesses to x are observable behavior, and cannot be
optimized away, anymore than an 'std::cout << "Hin"' could be optimized
away. If a and b are not volatile, there is no observable behavior, and
the compiler has pretty much total liberty.

Quote:
If a compiler can determine that repeating th eassignment every time
makes no difference, does it still have to do it?

Only if the assignment is to a volatile variable.

Quote:
Why doesn't C++ give the guarantee that C does in 5.1.2.3 para 3?

It does. The differences in wording of C §5.1.2.3/3 and C++ §1.9/7 are
very minor, and don't change the guarantee in any way.

Quote:
In 7.1.5.1 para 8 there is an unfortunate statement that volatile is a hint to the implementation to avoid aggressive
optimization 1.9 has detailed semantics. What would be the difference between
aggressive and non-aggressive optimisation? This sentence in 7.1.5.1
seems thoughtless and loose - if volatile is just a hint, then 1.9
should say so.

C++ basically copies C in this respect. In the end, the standard (C or
C++) makes the intent of volatile clear, without imposing semantics
which could be inappropriate for certain platforms.

My personal feeling is that most modern implementations don't even
respect the intent -- that the intent of volatile IS that if processor A
modifies a volatile object, and processor B accesses it after the
modification, processor B will see the modified version. Provided, of
course, that the access is atomic -- what types support atomic access,
and under what conditions, must obviously be implementation defined.
But that's my opinion with regards to the intent, and it is NOT what
most current compilers implement.

Quote:
[snip]

In an interrupt handler you will have to use platform-specific
primitives. At a very low level there are memory barriers to
control the order of memory access and on some processors there are
locked operations. A slightly higher level construct is the
spin-lock, which can almost substitute for a mutex but will never
sleep. However, to avoid deadlock, you have to disable the
interrupt when obtaining the spin-lock in non-interrupt code. This
is hairy stuff.

Where you don't have to share data with interrupt handlers, you can
still use those but you will generally do best to stick to mutexes.
Typical mutex implementations will spin for a while before sleeping
on SMP machines.

Fortunately I'm writing code for an embedded application and don't
have to worry about multiple processors. In a multiple processor
environment I wouldn't attempt to write "low level" software.

Well, someone has to do it:-).

Seriously, that was the intent of my question. If you are concerned
with threading, under Unix or Windows, then volatile is of no interest
to you -- it is neither sufficient nor necessary. If you are concerned
with handling hardware interrupts, you are working at a far different
level, and it is very likely that volatile IS what you need. A busy
wait on a hardware interruption, for example, doesn't really make sense
except on a single processor, the one which handles the interruption.
And all of the hardware implementations I am aware of do ensure
processor self-consistency -- within a single processor, any read will
see the results of the last preceding write to the same address (where
the order of operations is defined by the sequence of the machine
instructions executed). The standard "definition" for access -- that
the compiler emits a machine instruction which "accesses" memory, is
sufficient.

It would still be worth verifying exactly what can be accessed with
guaranteed atomicity.

Quote:
The class an_atomic_int below will provide the atomic access and
can change between platforms but all other code that uses it
should work correctly without being changed.

Where you have comments with assembly-language code, do you mean
that something along those lines should be substituted in place of
the following line, or that you expect the compiler to generate
that?

The first - it was supposed to be "pseudo code" that provided atomic
access. e.g. on the Hitachi micro we're using, a 32 bit "long"
instruction does an atomic read.

Does the fact that k is a volatile type guarantee that in
function f1() below, that at line **1, the get() function is
called repeatedly and not once only, and that after the statement
in line **1 is complete, there will be no further access to the k
object i.e. that in line **2, the three accesses of j1 will all
use the same value?

volatile-qualification has no useful effect on class-types,

Yes it does. It means that all members of an object of that type have
to be treated as volatile.

I should have said, "useful semantics". It propagates down until it
encounters a non-class type. But the semantics are those of the
individual non-class types.

In your example, you declared the only relevant class member volatile.
That was sufficient. All of the other volatiles had no effect what so
ever.

Quote:
but on objects of volatile-qualified built-in types it does mean
(roughly) that the implementation is required to generate code that
accesses them in the order implied by the source code (so far as
that is specified by the standard). So the answer is no, the fact
that k.v has a volatile-qualified type guarantees that.

( f1() is just for illustration - in practice it would have a non-busy
wait).
The class an_atomic_int has no operator int() function, but I might have
a second version that does have one.

Is it common practice in a Win32 X86 multithreading environment,
to assume that accessing an int or pointer is atomic and can be
read or written from multiple threads without synchronisation
(since X86 32 bit data access is known to be an atomic operation
on Pentium 2 or better for both aligned and non aligned data ??).

It is atomic, but that doesn't mean it can't be re-ordered with
respect to other atomic operations. To build larger atomic
operations you need memory barriers. On x86 I believe these are
implied by the lock-prefix on instructions.

This would presumably be unreliable in a situation where the
compiler might "secretly" access the variable twice e.g. a*b
might appear to access "a" once only but it could be accessed
multiple times (e.g. to check for negativity separately from the
value).

Some compilers have intrinsic functions for memory barriers and will
avoid re-ordering across them. Any compiler that supports multi-
threading must avoid re-ordering access to static or heap objects
across calls into functions for which it can't "see" the source,
which will be the case for at least the critical parts of
synchronisation primitives.

I'm a little lost here. What do you mean by a "compiler that supports
multithreading". Do you happen to know how thread safety of heap
memory is generally managed - do all heap management functions use a
mutex? What do you mean by "re-ordering access" - do compilers have a
"multi-threaded" mode and "non multi-threaded" mode?

All of the implementations of malloc I know use a mutex in the
multithreaded version. (All of the implementations of operator new that
I know use malloc.)

Compilers do have a multithreaded and a single threaded mode. For most
processors, the differences are perhaps minimum in the code generation,
especially since most compilers don't optimize particularly agressively
(accross module boundaries) anyway.

Don't forget too, that on modern processors, hardware may reorder
accesses, regardless of what the compiler does. Most modern processors
have special machine instructions (membar on a Sparc, for example) to
impose ordering in specific cases. Posix requires that a certain number
of functions (such as pthread_mutex_lock) issue these instructions, so
that if two threads are synchronized, thread B, having acquired the
mutex, will see all of the modifications made by thread A, which just
released the lock, even if the two threads are running on separate
processors -- in other words, somewhere in the code for
pthread_mutex_lock and pthread_mutex_unlock, there must be memory
barrier instructions (depending on the actual hardware architecture).

None of the compilers I know issue such instructions for volatile.
IMHO, this is in violation of the intent of volatile, but it is the
actual situation, and unless you intend to write your own compiler, we
have to live with it.

--
James Kanze GABI Software mailto:kanze (AT) gabi-soft (DOT) fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Tue Feb 17, 2004 9:41 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

Graeme Prentice <invalid (AT) yahoo (DOT) co.nz> wrote


Quote:
Does the fact that k is a volatile type guarantee that in
function f1() below, that at line **1, the get() function is
called repeatedly and not once only, and that after the statement
in line **1 is complete, there will be no further access to the k
object i.e. that in line **2, the three accesses of j1 will all
use the same value?

Yes to both questions.


So is this 1.9 para 7 that gives this guarantee.
quote
Accessing an object designated by a volatile lvalue (3.10), modifying an
object, ... [snip] are all side effects. ... at sequence points, ...
all side effects of previous evaluations shall be complete
end quote

Does this mean the compiler is prohibited from accessing a volatile
lvalue beyond the next sequence point from where it is referenced by
the abstract machine? The Harbison & Steele C book I have suggests
this might not be the case (I'm not sure).

I believe that this paragraph is talking about the definition of the
semantics of the abstract machine. Before application of the as-if
rule. The result is that the observable behavior of the entire
program
must be "as if" the generated code obeyed this paragraph. (Note that
the paragraph speaks of "modifying an [non-volatile] object".)

Accessing a volatile object is observable behavior, so it cannot be
optimized out nor reordered. And while paragraph 11 speaks of a
requirement that volatile objects be stable at a sequence point,
sequence points are NOT externally observable, so the only real
guarantee is that the order is respected. (IMHO, the wording of this
paragraph is extremely unfortunate.)

Quote:
I realize that the meaning of "accessed" is implementation-defined but
for the compiler we're using (gcc) there is a detailed description of
what accessed means in this situation
http://gcc.gnu.org/onlinedocs/gcc-3.3.2/gcc/Volatiles.html#Volatiles

and Microsoft similarly on MSDN
http://makeashorterlink.com/?A60224867
http://makeashorterlink.com/?X17223867

Interesting, because none of the above pages actually say anything
about
what is meant by an "access". In all cases, the documentation only
describes under what conditions the compiler considers an access
necessary.

The standard requires that a conforming compiler document all
"implementation defined" behavior. If there is no more documentation
than this concerning volatile accesses, neither G++ nor VC++ are
conforming.

Quote:
From having carefully looked at the generated code from g++, on a Sun
Sparc under Solaris, I can say that g++ pretty much ignores the issue;

as far as g++ is concerned, issuing a load or a store machine
instruction is sufficient for "access". Given the Sparc memory
architecture, that's a pretty wishy-washy definition, and more or less
useless in a multiprocessor environment.

[...]
Quote:
Questions about what it common practice in Win32 programs should be
directed to a Windows programming or Microsoft support group.

I was trying to make a point relevant to all C++ programs, using a
common platform that just happens to have an atomic int as an example,
that the fact that a data type is atomic doesn't guarantee that it's
safe to share between threads and that to get the safety, a mutex may
be needed, which has non trivial affect on performance and
"throughput" of a thread.

That's a know problem. On many platforms, there are cheaper ways.
(See, for example,
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/interlockedincrement.asp
for Windows.)

Quote:
However, if volatile provides the semantics you say, then in some
cases, a volatile atomic data type can be shared safely without a
mutex.

If the implementation defines access adequately, a volatile atomic
data
type can be shared safely without a mutex. G++ doesn't. Sun CC
doesn't. VC++ doesn't. In fact, I don't know of a compiler which
does.

Quote:
For the expression a*b where a is volatile and atomic, the compiler is
entitled to read "a" multiple times during the evaluation of a*b.

No. The compiler is entitle to define "read" as it wishes, but
whatever
definition it uses, a can only be read once.

Quote:
Similarly for cout << a, "a" can be read multiple times - the fact
that "a" is passed by value to operator<< probably makes this safe.
However, if I write x=a; y = x*b; then the evaluation of x*b should
work correctly if the compiler is prohibited from accessing "a" again
after the statement x=a;

If "a" is memory mapped IO, what prevents the compiler from reading
the IO twice when evaluating x=a; for volatile "a".

If the compiler supports memory mapped IO, presumably, it provides an
adequate definition for "read". In fact, the problems occur because
the
compilers don't do anything special to prevent hardware level
optimizations. And normally, the hardware will recognize that the
address is IO, and not reorder or otherwise optimize (e.g. by putting
the values in its local cache). So normally, I would expect that
using
volatile works for memory mapped IO.

Quote:
If "a" is a byte, then of course there's no reason to read it twice so
the code is safe enough but does the C or C++ language prohibit
multiple reads - if so, where?

§1.9/5: "A conforming implementation executing a well-formed programm
shall produce the same observable behavior as one of the possible
execution sequences of the corresponding instance of the abstract
machine with the same program and the same input." §1.9/6: "The
observable behavior of the abstract machine is its sequence of reads
and
writes to volatile data and calls to library I/O functions."

The semantics of the abstract machine read a once in a*b. If a is
volatile, this behavior is observable, and must be respected.

--
James Kanze GABI Software mailto:kanze (AT) gabi-soft (DOT) fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Graeme Prentice
Guest





PostPosted: Wed Feb 18, 2004 12:09 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

On 17 Feb 2004 16:41:18 -0500, [email]kanze (AT) gabi-soft (DOT) fr[/email] wrote:

Quote:
Graeme Prentice <invalid (AT) yahoo (DOT) co.nz> wrote in message

Does this mean the compiler is prohibited from accessing a volatile
lvalue beyond the next sequence point from where it is referenced by
the abstract machine? The Harbison & Steele C book I have suggests
this might not be the case (I'm not sure).

I believe that this paragraph is talking about the definition of the
semantics of the abstract machine. Before application of the as-if
rule. The result is that the observable behavior of the entire
program
must be "as if" the generated code obeyed this paragraph. (Note that
the paragraph speaks of "modifying an [non-volatile] object".)

Accessing a volatile object is observable behavior, so it cannot be
optimized out nor reordered. And while paragraph 11 speaks of a
requirement that volatile objects be stable at a sequence point,
sequence points are NOT externally observable, so the only real
guarantee is that the order is respected. (IMHO, the wording of this
paragraph is extremely unfortunate.)


The C++ standard seems to make it clearer than C (to me) because of the
short sentence in 1.9 para 6 and footnote 5 about the observable
behaviour.

Quote:

I was trying to make a point relevant to all C++ programs, using a
common platform that just happens to have an atomic int as an example,
that the fact that a data type is atomic doesn't guarantee that it's
safe to share between threads and that to get the safety, a mutex may
be needed, which has non trivial affect on performance and
"throughput" of a thread.

That's a know problem. On many platforms, there are cheaper ways.
(See, for example,
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/interlockedincrement.asp
for Windows.)


That web page suggests/states that InterlockedIncrement works
successfully/correctly on all hardware that Windows runs on, including
multiprocessor.


[snip]

Quote:

If "a" is memory mapped IO, what prevents the compiler from reading
the IO twice when evaluating x=a; for volatile "a".


[snip]

Quote:

§1.9/5: "A conforming implementation executing a well-formed programm
shall produce the same observable behavior as one of the possible
execution sequences of the corresponding instance of the abstract
machine with the same program and the same input." §1.9/6: "The
observable behavior of the abstract machine is its sequence of reads
and
writes to volatile data and calls to library I/O functions."

The semantics of the abstract machine read a once in a*b. If a is
volatile, this behavior is observable, and must be respected.

ok. I think this could be pointed out in the C++ standard - that a
single read of "a" is implied by the abstract machine which translates
into the observable behaviour sequnce for volatile a.

The GCC web page says
<quote>
"Thus an implementation is free to reorder and combine volatile accesses
which occur between sequence points"
<end quote>

and the reordering is allowed because the abstract machine execution
leaves it unspecified.

For a*a, the abstract machine implies two reads of "a" but GCC says
volatile accesses can be combined between sequence points. It's not
especially clear to me that "a" can't be read more than once when
evaluating a*b for volatile "a".

Anyway, I will tread carefully. Thanks for all your replies James.

Graeme

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Graeme Prentice
Guest





PostPosted: Wed Feb 18, 2004 12:10 pm    Post subject: Re: volatile, atomic variables & multithreading Reply with quote

On 17 Feb 2004 16:40:04 -0500, [email]kanze (AT) gabi-soft (DOT) fr[/email] wrote:


[snip]

Quote:
If a compiler can determine that repeating th eassignment every time
makes no difference, does it still have to do it?

Only if the assignment is to a volatile variable.

Why doesn't C++ give the guarantee that C does in 5.1.2.3 para 3?

It does. The differences in wording of C §5.1.2.3/3 and C++ §1.9/7 are
very minor, and don't change the guarantee in any way.


ok. I missed the fact that the C standard in 5.1.2.3/3 says an
expression does not have to be evaluated if it has no needed side
effects (side effects that aren't needed don't have to be executed.),
and I had a complete mental block about the meaning of C++ standard 1.9
para 6 on the observable behaviour.


[snip]

Quote:

C++ basically copies C in this respect. In the end, the standard (C or
C++) makes the intent of volatile clear, without imposing semantics
which could be inappropriate for certain platforms.

ok, so this is what you mean by implementation defined. I didn't
realise there were platforms on which a write to a volatile variable
might not be seen by a later read in a different thread.

[snip]
Quote:

In your example, you declared the only relevant class member volatile.
That was sufficient. All of the other volatiles had no effect what so
ever.

ok.

[snip]
Quote:

Don't forget too, that on modern processors, hardware may reorder
accesses, regardless of what the compiler does. Most modern processors
have special machine instructions (membar on a Sparc, for example) to
impose ordering in specific cases. Posix requires that a certain number
of functions (such as pthread_mutex_lock) issue these instructions, so
that if two threads are synchronized, thread B, having acquired the
mutex, will see all of the modifications made by thread A, which just