On Dec 15, 7:48 am, Johannes Bauer <dfnsonfsdu...@gmx.dewrote:
Mark F. Haigh schrieb:
On Dec 15, 6:04 am, CJ <nos...@nospam.invalidwrote:
The following tries to make thread-safe code while avoiding the high
cost of a lock. But is it kosher?
// file or global scope level
int X;
volatile _Bool IsInitializedX=false;
// thread 1
X=5;
IsInitializedX=true;
// thread 2
while(!IsInitializedX)
;
printf("Now safe to read X=%d\n", X);
No. Don't do that. Semi-OT discussion follows.
On most systems, volatile simply means "don't optimize away-- actually
issue the loads and stores from the CPU's perspective". However,
these loads and stores can get hung up in the memory hierarchy for
indefinite amounts of time.
Why is that?
It's dictated by the design of the hardware.
"volatile" should mean the variable in question might be
read by another thread without the compiler knowing when that might
happen.
That's not quite correct-- it's much more loosely defined than that.
Essentially, expressions involving a volatile object are evaluated
strictly according the the C Abstract Machine, and side effects are
preserved.
This has two implications: 1. Don't optimize it away. 2. Do all
load/store operations immediately without keeping it in a register.
Correct. Above, I refer to #2 as "issue the loads and stores from the
CPU's perspective". Perhaps I could have phrased that as "minimize
register caching, issuing loads and stores from the CPU as soon as
possible".
This applies especially for the typical while (!initialized); loop,
which without initialized may be optimized to (some pseoducode follows).
C:
int initialized=0;
while (initialized);
Unoptimized asm without volatile:
movl initialized, %eax
moo:
test %eax
jz moo
(essentially while (1);)
Asm with volatile:
moo:
movl initialized, %eax
test %eax
jz moo
So not only is the variable not optimized away, but the memory access is
always immediate.
You're missing the point-- the movl instruction may fetch its memory
from anywhere in the system memory hierarchy. This could be main
memory, L3, L2, L1, or anything in between, and these caches can have
wildly different coherency mechanisms, including none at all. It's
all up to the decisions of the system hardware architects.
Even simply reading the value could evict some other random cache
entry from one of the caches as a side effect, resulting in a write-
back to another level in the caching hierarchy. This write-back could
be eventually sent through a coalescing write buffer, depending on how
decoupled the system designers want to keep the CPUs from each other
and everything else.
The problem here is that "memory access" is very poorly defined. The
only thing C can do about it is to say (in a roundabout manner) "have
the CPU issue the loads or stores immediately, and what happens after
that is out of our hands".
In any given system, there are system-specific rules that govern when
stores are visible to other components (which include different
physical CPUs, neighboring CPU cores, logical CPU cores, IO devices,
different levels of caches and buffers, and system core logic /
"chipsets").
Marking something as volatile will tell the CPU to issue the stores,
but there is no guarantee about when and where this store will be
visible to other threads, any of which can be running on other CPUs.
>
Another example is the AVR architecture. Memory mapped IO for port
register access is done by use of volatile memory pointers:
#define PORTB ((volatile unsigned char*)0x123)
So that code like this:
while (1) {
PORTB &= ~(1 << 4);
PORTB |= (1 << 4);
}
Is guaranteed to output code which *always* makes a store to the
according register.
<snip>
It's guaranteed by your particular implementation, not guaranteed by
C. It may presuppose, for instance, that 0x123 is located in a
noncacheable IO region and writes to that region are not coalesced.
Otherwise it's quite conceivable that the __system hardware__ (not the
generated code) may see a redundant write and only actually "commit"
the second one. Identical assembly code may work in some cases and
fail in others, depending on the vagaries of the hardware.
Mark F. Haigh
mf*****@sbcglobal.net