Jump to content

  • Log In with Google      Sign In   
  • Create Account


#ActualHodgman

Posted 11 December 2012 - 07:57 AM

Someone just down-voted all my posts in this thread about volatile -- I'd appreciate being told why I'm wrong as well as that I am wrong, so I (and others) can learn from my mistake.


To explain why I cringe at the sight of volatile in C++ code that isn't interacting with special hardware registers or memory mappings:

Volatile is so often misunderstood that some code-bases check for it in commit logs and automatically flag it as a bug.
See: http://kernel.org/do...red-harmful.txt
See also the "double checked locking is broken" fiasco.

Volatile isn't required in threaded code if you're already using synchronisation primitives like mutexes or semaphores.

Carnegie Mellon teach in their secure coding materials, not to use volatile as a synchronisation primitive, because it's not required to work as one.

It's taught that when using POSIX threads, you shouldn't ever use volatile, because pthreads' synchronisation primitives make it unnecessary.

HP/IBM employees opine to remind us that volatile isn't required if using synchronisation primitives, that volatile variables aren't necessarily atomic, and that the actual details are always platform specific anyway.

When it's used as a synchronisation primitive in threaded code, it's often incorrect, for example:
int data = 42;
volatile bool ready = false;
void Thread1()
{
  data = 100;
  ready = true;
}
void Thread2()
{
  while(!ready){Sleep();}
  printf("%d", data );
}
The above code will print 100 on a 2005+ Microsoft compiler, but is not required to do so according to the C++ standard. A compliant C++ compiler will likely print 100 or 42, but could print anything. The write of "ready = true" is allowed to become visible to other threads before the write of "data = 100" does. N.B. even if you make data volatile, the order that the changes become visible in Thread2 is still unspecified.

You can't correctly implement the above synchronisation using just volatile. Maybe this works on your compiler and your CPU, but it's not required to. Because of this, if you're writing multi-threaded code that uses volatile to synchronise data between threads, you've likely got a bug (maybe not on your current platform if you're lucky).

In order to correctly implement thread synchronisation in the above example, you need to be aware of the memory fences required on your target hardware (or use C++11's atomic and memory ordering functionality); volatile doesn't address these issues at all... Well, on MSVC it does (MSVC inserts full fences around volatile accesses, well beyond what is required by the spec), which is very bad IMHO, because this causes people write incorrect code which happens to work until they port it to another platform...

To paraphrase Herb Sutter, if you're looking for volatile in C++, you probably really want atomic.

#2Hodgman

Posted 11 December 2012 - 06:16 AM

Someone just down-voted all my posts in this thread about volatile -- I'd appreciate being told why I'm wrong as well as that I am wrong, so I (and others) can learn from my mistake.


To explain why I cringe at the sight of volatile in C++ code that isn't interacting with special hardware registers or memory mappings:

Volatile is so often misunderstood that some code-bases check for it in commit logs and automatically flag it as a bug.
See: http://kernel.org/do...red-harmful.txt
See also the "double checked locking is broken" fiasco.

Volatile isn't required in threaded code if you're already using synchronisation primitives like mutexes or semaphores.

Carnegie Mellon teach in their secure coding materials, not to use volatile as a synchronisation primitive, because it's not required to work as one.

It's taught that when using POSIX threads, you shouldn't ever use volatile, because pthreads' synchronisation primitives make it unnecessary.

HP/IBM employees opine to remind us that volatile isn't required if using synchronisation primitives, that volatile variables aren't necessarily atomic, and that the actual details are always platform specific anyway.

When it's used as a synchronisation primitive in threaded code, it's often incorrect, for example:
int data = 42;
volatile bool ready = false;
void Thread1()
{
  data = 100;
  ready = true;
}
void Thread2()
{
  while(!ready){Sleep();}
  printf("%d", data );
}
The above code will print 100 on a 2005+ Microsoft compiler, but is not required to do so according to the C++ standard. A compliant C++ compiler will likely print 100 or 42, but could print anything. The write of "ready = true" is allowed to become visible before the write of "data = 100" does. N.B. even if you make data volatile, the order that the changes become visible in Thread2 is still unspecified.

You can't correctly implement the above synchronisation using just volatile. Maybe this works on your compiler and your CPU, but it's not required to. Because of this, if you're writing multi-threaded code that uses volatile to synchronise data between threads, you've likely got a bug (maybe not on your current platform if you're lucky).

In order to correctly implement thread synchronisation in the above example, you need to be aware of the memory fences required on your target hardware (or use C++11's atomic and memory ordering functionality); volatile doesn't address these issues at all... Well, on MSVC it does (MSVC inserts full fences around volatile accesses, well beyond what is required by the spec), which is very bad IMHO, because this causes people write incorrect code which happens to work until they port it to another platform...

To paraphrase Herb Sutter, if you're looking for volatile in C++, you probably really want atomic.

#1Hodgman

Posted 11 December 2012 - 06:00 AM

Someone just down-voted all my posts in this thread about volatile -- I'd appreciate being told why I'm wrong as well as that I am wrong, so I (and others) can learn from my mistake.


To explain why I cringe at the sight of volatile in C++ code that isn't interacting with special hardware registers or memory mappings:

Volatile is so often misunderstood that some code-bases check for it in commit logs and automatically flag it as a bug.
See: http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
See also the "double checked locking is broken" fiasco.

Volatile isn't required in threaded code if you're already using synchronisation primitives like mutexes or semaphores.

Carnegie Mellon teach in their secure coding materials, not to use volatile as a synchronisation primitive, because it's not required to work as one.

It's taught that when using POSIX threads, you shouldn't ever use volatile, because pthreads' synchronisation primitives make it unnecessary.

HP/IBM employees opine to remind us that volatile isn't required if using synchronisation primitives, that volatile variables aren't necessarily atomic, and that the actual details are always platform specific anyway.

To paraphrase Herb Sutter, if you're looking for volatile, you probably really want atomic.

When it's used as a synchronisation primitive in threaded code, it's often incorrect, for example:
int data = 42;
volatile bool ready = false;
void Thread1()
{
  data = 100;
  ready = true;
}
void Thread2()
{
  while(!ready){Sleep();}
  printf("%d", data );
}
The above code will print 100 on a 2005+ Microsoft compiler, but is not required to do so according to the C++ standard. A compliant C++ compiler will likely print 100 or 42, but could print anything. The write of "ready = true" is allowed to become visible before the write of "data = 100" does.
In order to correctly implement thread synchronisation in the above example, you need to be aware of the memory fences required on your target hardware (or use C++11's atomic and memory ordering functionality), volatile doesn't address these issues at all... Well, on MSVC it does (MSVC inserts full fences around volatile accesses, well beyond what is required by the spec), which is very bad IMHO, because this causes people write incorrect code which happens to work until they port it to another platform...

PARTNERS