How could I know a variable is read from memory not register without using volatile.
static uint64 counter;
void thread(void *arg){
while(counter < (uint64)arg){
acquire(&lock)
counter ;
release(&lock);
}
}
I never doubt with that before, but if there's compiler optimization, won't it produce bad result. I also find the volatile is rare in xv6 kernel code, and I wonder is there some tricks to suppress optimization through compiler's command line to prevent use of volatile.
CodePudding user response:
You don't need volatile
as long as your locking library is properly implemented - it will (by design) guarantee the correct behavior in spite of compiler optimizations, threads contending on the same resource, scheduling on other processors and cores, etc.
A properly-implemented acquire
and release
will guarantee that the compiler won't produce undesired reorderings, and more importantly, it will also guarantee that a processor (and its associated cache controller) won't produce undesired reorderings (which can occur on some processor architectures, even if the compiler emits the desired sequence of operations).
One approach to doing this is with the use of barriers. I present it more as an explanation of how things work, and not as a recommendation to handwrite such synchronization code:
The
acquire
function includes a so-called memory barrier with "acquire semantics" - an acquire barrier means that any code following it (i.e. the code inside the critical section) must NOT be reordered to happen BEFORE the barrier. This means that any other threads and processors will necessarily see the acquisition of the lock before any of the writes from the critical section.The
release
function provides an analogous memory barrier that prevents anything in the critical section from being reordered AFTER the barrier. This ensures that other threads will see the effects from the critical section BEFORE they see the lock being released.
Unless you are writing a low-level library and are prepared to thoroughly prove, verify, and test it, you should not be writing this kind of barrier code yourself. It's very easy to make subtle mistakes, performance inefficiencies, etc.