Everything I've read about volatile says it's never safe, but I still feel inclined to try it, and I haven't seen this specific scenario declared unsafe.
I have a separate thread that renders a scene, pulling data from the main simulation thread. This has no synchronization, and works fine.
The issue is that when the program exits, then renderer needs to stop pulling data from the simulation thread before the simulation thread can safely clean itself up without causing the renderer to attempt reading invalid memory.
To accomplish this, I have the renderer run infinitely in its thread:
volatile bool stillRendering;
void RenderThreadFunction()
{
stillRendering = true;
while(programRunning)
{
renderer->render();
}
stillRendering = false;
}
In the main program thread, when the windproc quit message is received, I do:
void OnQuit()
{
programRunning = false;
while(stillRendering)
{
}
delete application;
}
The goal of this is to be sure the renderer stops pulling data from the application before calling delete on the application.
I first tried this without any volatile keywords, and it worked in debug mode, but in release mode it hung. I assume the compiler made some optimization that causes the program to stop checking the value of stillRendering.
Adding volatile to just stillRendering caused the application to successfully exit everytime I've tested it so far. I'm not certain why it doesn't seem to matter if "programRunning" is volatile.
Lastly, I am uncertain how the performance of the program will be impacted by using volatile for "stillRendering". It doesn't matter to me if making stillRendering volatile affects the performance of OnQuit(), but it does matter to me if it affects the performance of RenderThreadFunction()
Updating a bool is always thread safe, if you never read from it. And if you do read from it, then the answer depends on when you read from it, and what that read signifies. On some CPUs, but not all, writes to an object of type bool will be atomic. x86 CPUs will generally make it atomic, but others might not.
In Java volatile creates a memory barrier when it's read, so it can be used as a threadsafe flag that a method has ended since it enforces a happens-before relationship with the code before the flag was set. This is not the case in C.
Therefore, the volatile keyword does not provide thread safety when non-atomic operations or composite operations are performed on shared variables. Operations like increment and decrement are composite operations.
A number of readers asked whether making the read into a volatile read prevented the reordering problem. The answer to the general question of whether a volatile read can be re-ordered is yes, yes it can.
It's completely unsafe, although it might work with some
compilers. Basically, volatile
only affects the variable it's
attached to, so RendererThreadFunction
, for example, could set
stillRendering
false before having finished
renderer->render();
. (This is true even if both
stillRendering
and programRunning
were both volatile.) The
probablility of a problem is very small, so testing probably
won't reveal it. And finally, some versions of VC++ do give
volatile
the semantics of an atomic access under C++11, in
which case, your code will work. (Until you compile with
a different version of VC++, of course.)
Given that renderer->render()
almost certainly takes
a non-negligible amount of time, there's absolutely no reason
for not using a conditional variable here. About the only time
you'd use volatile
for this sort of thing is if the shutdown
mechanism were triggered by a signal (in which case, the type
would be sig_atomic_t
, and not bool
, although in practice,
it probably doesn't make any difference). In that case, there
wouldn't be two threads, but just the renderer thread and
a signal handler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With