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()
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.