Volatile classes

M

mlimber

I am using placement new to locate a shared data structure at a
particular location in shared memory. The problem is that when I access
the data on one of the two processors sharing it, I don't get the same
values seen by the other processor (which uses a cache-less access
method, unlike what follows). Here's a reduced example:

struct SharedData
{
enum Item { A=0, B=1 };

// Note volatile:
int Get( Item i ) const volatile { return data_[ i ]; }
// ...
private:
int data_[ 2 ];
};

void Foo()
{
void *const addr = reinterpret_cast<void*>( 0x8000 );
volatile SharedData *const data = new( addr ) SharedData;

const int a1 = data->Get( SharedData::A );
const int a2 = data->Get( SharedData::A );
// ...
}

The problem is that a1 and a2 may not be the same because the second
processor may modify the value - a classic case for the volatile
qualifier. The C way to handle this would be something like:

volatile int* const a = reinterpret_cast<volatile int*>( 0x8000 );
volatile int* const b = reinterpret_cast<volatile int*>( 0x8004 );

I thought, however, that applying volatile to the pointer-type in Foo()
and the appropriate member functions in SharedData would do basically
the same thing - namely, force all the data members of SharedData to be
volatile. Am I mistaken?

Cheers! --M
 
P

peter steiner

mlimber said:
I am using placement new to locate a shared data structure at a
particular location in shared memory. The problem is that when I access
the data on one of the two processors sharing it, I don't get the same
values seen by the other processor (which uses a cache-less access
method, unlike what follows). Here's a reduced example:

struct SharedData
{
enum Item { A=0, B=1 };

// Note volatile:
int Get( Item i ) const volatile { return data_[ i ]; }
// ...
private:
int data_[ 2 ];
};

void Foo()
{
void *const addr = reinterpret_cast<void*>( 0x8000 );
volatile SharedData *const data = new( addr ) SharedData;

const int a1 = data->Get( SharedData::A );
const int a2 = data->Get( SharedData::A );
// ...
}

The problem is that a1 and a2 may not be the same because the second
processor may modify the value - a classic case for the volatile
qualifier. The C way to handle this would be something like:

volatile int* const a = reinterpret_cast<volatile int*>( 0x8000 );
volatile int* const b = reinterpret_cast<volatile int*>( 0x8004 );

I thought, however, that applying volatile to the pointer-type in Foo()
and the appropriate member functions in SharedData would do basically
the same thing - namely, force all the data members of SharedData to be
volatile. Am I mistaken?

i don't have the standard at hand, but according to the following link,
a fantastic article about this topic by alexandrescu, it does.

http://www.cuj.com/documents/s=7998/cujcexp1902alexandr/

to me this looks like either your memory is modified by other effects
or this is a compiler bug.

-- peter
 
G

Gianni Mariani

mlimber wrote:
....
The problem is that a1 and a2 may not be the same because the second
processor may modify the value - a classic case for the volatile
qualifier. The C way to handle this would be something like:

volatile int* const a = reinterpret_cast<volatile int*>( 0x8000 );
volatile int* const b = reinterpret_cast<volatile int*>( 0x8004 );

I thought, however, that applying volatile to the pointer-type in Foo()
and the appropriate member functions in SharedData would do basically
the same thing - namely, force all the data members of SharedData to be
volatile. Am I mistaken?

What do you think volatile means ?

I don't understand what the "problem" is.
 
M

mlimber

Gianni said:
mlimber wrote:
...

What do you think volatile means ?

It is compiler specific, but it means that the compiler should not do
any fancy optimizations to the data at hand. That means, for instance,
not caching a variable in a register even though the code doesn't
appear to change it. By using volatile, the programmer is indicating
that the value could change due to something outside the program (e.g.
another thread or process or a hardware device).
I don't understand what the "problem" is.

It appears that my volatile data is not read fresh at each access.
Consequently, one processor incorrectly reads 0 while the other
correctly reads 1. (Correctness is determined by what the memory
actually holds.)

Cheers! --M
 
K

Kaz Kylheku

mlimber said:
I am using placement new to locate a shared data structure at a
particular location in shared memory. The problem is that when I access
the data on one of the two processors sharing it, I don't get the same
values seen by the other processor (which uses a cache-less access
method, unlike what follows).

There is nothing in the C++ language that can help you. The C++
standard doesn't require any support for multiprocessing.
The problem is that a1 and a2 may not be the same because the second
processor may modify the value - a classic case for the volatile
qualifier.

That is not what volatile is intended for.

The intent of volatile is to suppress the /language implementation's/
caching optimizations. Not those of the entire data processor. Caching
optimizations are deviations from the abstract machine, whereby objects
that are modified are not actually updated at the next sequence point.

There is always /some/ caching going on. If you load a value into
processor, and do something with it, then compute a new value, for a
brief time, the computed value is out of sync with the storage object
that it's destined for.

The volatile qualifier comes from ANSI C. According to ANSI C, an
asynchronous signal handler may write a value to a static object of
type volatile sig_atomic_t. The idea is that mainline code which checks
that value will see the modification, rather than keep referring to a
cached copy. This example doesn't even involve multiple threads, never
mind processors. You see, the caching optimizations performed by
compilers can affect the interaction between single-threaded code and
its asynchronous interrupts, or threads that are scheduled on just one
processor that has a single, coherent view of its memory.

Other than making this case work (volatile sig_atomic_t and signal
handler), a compiler can ignore volatile (provided that it emits a
diagnostic about any diagnosable rule violation related to the use of
the keyword).

At best, if your compiler properly supports the spirit of the intended
meaning of volatile, its use will ensure that when you store to the
object, the value is actually written to memory no later than the next
sequence point, and when you read from the object, the latest value is
retrieved.

If you need further synchronization at the hardware level, you have to
insert the machine instructions yourself: memory barriers, cache
flushes, whatever.
I thought, however, that applying volatile to the pointer-type in Foo()
and the appropriate member functions in SharedData would do basically
the same thing - namely, force all the data members of SharedData to be
volatile. Am I mistaken?

Nope. The qualifier on a member function is essentially inherited by
the this pointer. If the function is const, the this pointer refers to
a const-qualified object, etc. This part is right.
 
K

Kaz Kylheku

mlimber said:
It is compiler specific, but it means that the compiler should not do
any fancy optimizations to the data at hand.

Multiprocessor coherency problems are not caused by optimizations that
the compiler does.
 
J

Josh Mcfarlane

mlimber said:
It is compiler specific, but it means that the compiler should not do
any fancy optimizations to the data at hand. That means, for instance,
not caching a variable in a register even though the code doesn't
appear to change it. By using volatile, the programmer is indicating
that the value could change due to something outside the program (e.g.
another thread or process or a hardware device).


It appears that my volatile data is not read fresh at each access.
Consequently, one processor incorrectly reads 0 while the other
correctly reads 1. (Correctness is determined by what the memory
actually holds.)

Have you tried adding volatile to the actual array rather than the
function call?
 
G

Gianni Mariani

mlimber said:
Gianni Mariani wrote:


It appears that my volatile data is not read fresh at each access.
Consequently, one processor incorrectly reads 0 while the other
correctly reads 1. (Correctness is determined by what the memory
actually holds.)

OK - This one is easy to check to see - look at the compiled
instructions and see if the generated code is actually performing the reads.

If I read it correctly, then yes, the _data array should be volatile in
this case.

Which compiler ?
 
M

mlimber

Gianni said:
OK - This one is easy to check to see - look at the compiled
instructions and see if the generated code is actually performing the reads.

If I read it correctly, then yes, the _data array should be volatile in
this case.

Which compiler ?

Texas Instruments 5.1.0, which I think uses the EDG front-end.

More to come when I try the suggestions given here...

Cheers! --M
 
M

mlimber

Gianni said:
OK - This one is easy to check to see - look at the compiled
instructions and see if the generated code is actually performing the reads.

If I read it correctly, then yes, the _data array should be volatile in
this case.

Which compiler ?

Upon further testing, the compiler seems to be doing its job correctly
and my code works as expected if I disable the user-configurable
hardware cache. Since I want to use the cache in the production code,
however, I'll need to look into the platform-specific details of making
things work with the cache enabled.

Cheers! --M
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,955
Messages
2,570,117
Members
46,705
Latest member
v_darius

Latest Threads

Top