Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Doug Harrison, please!

128 views
Skip to first unread message

fh1996

unread,
Feb 7, 2004, 12:13:02 PM2/7/04
to
Thanks to everyone who answered my questions about SEH, specifically
"volatile" one!
(Please see thread: SEH and standard C++ exceptions)

I have some more questions, which is regarding Doug Harrison's post:

// Assuming everyone observes the locking protocol, this approach
// would work on all hardware, even weird architectures like Sparc
// and others which require memory barriers:

bool x = true; // As above, just non-volatile

for (;;)
{
lock(mx);
bool y = x;
unlock(mx);
if (!y)
break;
whatever;
}

This is new and interesting to me. Is this solution better than using
"volatile bool x = true;"?

"2. You need volatile to avoid undefined behavior in certain signal handler
and setjmp/longjmp scenarios."

Would you mind to give us some examples about this?

"3. You can use volatile, say, to declare a pointer to a volatile int, which
represents a memory location updated at the interrupt level, something you
can't synchronize with, and which is outside the compiler's knowledge of the
program."

This is totally beyond my apprehension. Would you please elaborate more on
this point?


Carl Daniel [VC++ MVP]

unread,
Feb 7, 2004, 2:01:36 PM2/7/04
to
fh1996 wrote:
> Thanks to everyone who answered my questions about SEH, specifically
> "volatile" one!
> (Please see thread: SEH and standard C++ exceptions)
>
> I have some more questions, which is regarding Doug Harrison's post:
>
> // Assuming everyone observes the locking protocol, this approach
> // would work on all hardware, even weird architectures like Sparc
> // and others which require memory barriers:
>
> bool x = true; // As above, just non-volatile
>
> for (;;)
> {
> lock(mx);
> bool y = x;
> unlock(mx);
> if (!y)
> break;
> whatever;
> }
>
> This is new and interesting to me. Is this solution better than using
> "volatile bool x = true;"?

Better in that it's actually guaranteed to work on all multi-processing
systems, yes. As Doug mentioned, on some architectures (Sparc64 being one
of them) it will actually cause a hardware fault if you access a variable
from two different CPUs without an intervening memory barrier. The
OS-supplied synchronization mechanisms always include the necessary barrier
instructions to guarantee that this sort of idiom works. If you ever plan
to move your code to IA-64 (Itanium), you'll need to use this technique
because it too requires memory barriers between multi-CPU accesses to a
single memory location.

The volatile idiom works on IA-32 machines because these systems implement
strong cache coherency between processors in an SMP machine. This strong
cache coherency allows for fairly sloppy programming (from a
multi-processing standpoint), but it comes at a cost: the bus bandwidth
consumed by maintaining strong cache coherency between multiple processors
limits the scalability of IA-32 based machines (that's why you rarely see
more than 8 CPUs in an IA-32 machine). Highly scalable architectures, such
as Sparc and Itanium don't have a strong cache coherency characteristic as a
result programming for them requires more discipline but the resulting
systems scale better.

>
> "2. You need volatile to avoid undefined behavior in certain signal
> handler and setjmp/longjmp scenarios."
>
> Would you mind to give us some examples about this?

These are defined by the POSIX and C standards.

>
> "3. You can use volatile, say, to declare a pointer to a volatile
> int, which represents a memory location updated at the interrupt
> level, something you can't synchronize with, and which is outside the
> compiler's knowledge of the program."
>
> This is totally beyond my apprehension. Would you please elaborate
> more on this point?

I assume you mean comprehension, not apprehension...

Originally, volatile was intended to be used to help with low-level device
drivers which interface with memory-mapped hardware. Such hardware appears
in the memory address space, but isn't memory per-se. Instead, it's some
arbitrary functionality that can be read and/or written - but unlike memory,
with hardware there's generally no guarantee that if you write something to
a location that you'll read that same something back when you later read
from that location. Volatile tells the compiler that the variable may
change values in ways that the compiler can't know about, such as underlying
hardware.

In Doug's example, rather than being memory-mapped hardware, a volatile
variable might be used to communicate between code that runs as a result of
a hardware interrupt (which the compiler also doesn't know about) and some
other code. Again, this kind of usage will typically only appear if you're
writing "close to the metal", such as in embedded work or device driver
development.

HTH

-cd


Doug Harrison [MVP]

unread,
Feb 7, 2004, 3:02:11 PM2/7/04
to
fh1996 wrote:

>Thanks to everyone who answered my questions about SEH, specifically
>"volatile" one!
>(Please see thread: SEH and standard C++ exceptions)
>
>I have some more questions, which is regarding Doug Harrison's post:
>
>// Assuming everyone observes the locking protocol, this approach
>// would work on all hardware, even weird architectures like Sparc
>// and others which require memory barriers:
>
>bool x = true; // As above, just non-volatile
>
>for (;;)
>{
> lock(mx);
> bool y = x;
> unlock(mx);
> if (!y)
> break;
> whatever;
>}
>
>This is new and interesting to me. Is this solution better than using
>"volatile bool x = true;"?

"Better" is not quite how I would characterize it. On systems where it
works, it's probably slower than using volatile and no locking. On the other
hand, it's a portable approach and is required on multiprocessor systems
with weakly ordered memory system. On such a system, if you declare x
volatile and use it outside mutex protection, and write to it in a given
thread, threads executing on other CPUs might not observe the write
immediately, and they might observe a series of writes including writes to x
in various orders, not necessarily the same order in which they were made.
If you're really on the ball, you might be able to issue your own memory
barriers and forgo the mutex, but it's a lot simpler just to use the mutex,
which also removes the atomicity issue for things more complex than simple
types like bool. BTW, atomic access to bool, int, etc. is an assumption,
which isn't guaranteed by the language standards, so that's another way in
which using a lock-free volatile approach such as the one I gave last time
can be dangerously non-portable. This is why I qualified that example with,
"On amenable hardware like x86, this idiom can work, ..." To that you can
add, "amenable types, and amenable access patterns". For example,
incrementing a variable is not atomic.

On Windows, using an event object is often superior to using any kind of
bool, because:

1. You needn't synchronize access to an event.
2. You can use an event in WaitForMultipleObjects and other wait functions,
which can make it much easier to implement an "end thread now" concept. It
can turn polling loops into infinite waits, which are more efficient and
less complex to write.

>"2. You need volatile to avoid undefined behavior in certain signal handler
>and setjmp/longjmp scenarios."
>
>Would you mind to give us some examples about this?

It's in the C Standard. Those things don't really come up in C++
programming, and IMO they're not worth committing to memory. IIRC, the
setjmp/longjmp usage concerns variables modified in the same function after
it calls setjmp, such that longjmp restores control to the point prior to
which those variables were modified. Or something like that. :)

>"3. You can use volatile, say, to declare a pointer to a volatile int, which
>represents a memory location updated at the interrupt level, something you
>can't synchronize with, and which is outside the compiler's knowledge of the
>program."
>
>This is totally beyond my apprehension. Would you please elaborate more on
>this point?

Again, you don't see this in ordinary Windows programming, because you don't
have low-level access to hardware. I expect it can come up if you're writing
device drivers, though. If you're talking about DOS, which has no memory
protection at all, I guess an example would be a TSR/application combo,
where the app periodically polls a memory location the TSR uses to
communicate with it. If the compiler can determine there's no way for the
app to modify the variable between successive reads, it can read it once and
propagate that value through those reads. Declaring the variable volatile
suppresses that optimization.

--
Doug Harrison
Microsoft MVP - Visual C++

0 new messages