The SFML library initializes an audio device when the first object of type AudioResource
is created, and deinitializes it when the last one is destroyed.
I am trying to simplify that code, as I believe that the current solution using std::[shared/weak]_ptr
is overkill.
I proposed an alternative using a static
global variable. Here's a simplified version (the real one is protected by std::mutex
):
// AudioResource.hpp --------------------------------
struct AudioResource
{
AudioResource();
~AudioResource();
// ...
};
// AudioResource.cpp --------------------------------
#include "AudioResource.hpp"
static int deviceRC = 0;
AudioResource() { if (deviceRC++ == 0) { /* init device */ }; }
~AudioResource() { if (--deviceRC == 0) { /* deinit device */ }; }
// ...
I'm think the code above is OK and not subject to static order initialization fiasco, as the deviceRC
global static
is defined and accessed only in one TU.
My understanding is that -- as long as there are no dependencies between multiple global static
variables from different TUs -- there's no risk.
Am I correct, or is it possible that the code above results in undefined behavior by somehow accessing deviceRC
before it is initialized?
Is there any scenario where creating AudioResource
objects from multiple TUs or when using SFML as a shared library can cause issues?
Is the code safe even if deviceRC
does not get constant-initialized? E.g.,
// SomeOtherTU.cpp --------------------------------
static AudioResource audioResource;
// Could `deviceRC` be accessed while uninitialized here?
Other SFML maintainers have suggested using a function-scope static
variable here:
// AudioResource.cpp --------------------------------
#include "AudioResource.hpp"
static int& deviceRC()
{
static int result = 0;
return result;
}
AudioResource() { if (deviceRC()++ == 0) { /* init device */ }; }
~AudioResource() { if (--deviceRC() == 0) { /* deinit device */ }; }
// ...
Is the approach using a fuction-scope static
variable safer and/or necessary compared to the one using a file-scope static
variable?
If deviceRc
is statically initialized, the code is safe because:
If deviceRc
is not statically initialized, then you have a problem. audioResource
could indeed be initialized before deviceRc
, since the two variables are defined in different TUs. When audioResource
is being initialized, it's possible that it could see the value deviceRc
has prior to dynamic initialization, that is, zero. If deviceRc
has dynamic initialization, then presumably the initialization is more complicated than just setting it to zero. So, presumably, the program is not correct if deviceRc
's value is read before that dynamic initialization is complete.
The strategy with the function-local static guarantees that result
is initialized before it is accessed, and result
is destroyed after any static variable that could have accessed result
during its own construction. These guarantees will normally ensure that your code doesn't have any initialization or destruction order fiascos, though you can create one if you really try.