Let's say, that this is a function that serves several threads. They read kHKeys
that is not protected since Read-Read from the same memory-address is not a data-race.
But, on the 1st Read, kHKeys
is constructed. It is possible that during construction, another thread enters reentrantFunction function.
Is it necessary to construct kHKeys
before unleashing the threads that call simultaneously the reentrantFunction ?
Example:
int reentrantFunction(const std::wstring& key_parent_c)
{
// const 'static' means that kHKeys is constructed only once —
// The 1st the function is run — and put into a shared memory space.
// Otherwise, kHKeys is local and it must be constructed each time the function is called.
const static std::map<std::wstring, HKEY> kHKeys{ { L"HKEY_CURRENT_USER", HKEY_CURRENT_USER } ,
{ L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE } , { L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT } ,
{ L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG } , { L"HKEY_CURRENT_USER_LOCAL_SETTINGS", HKEY_CURRENT_USER_LOCAL_SETTINGS } ,
{ L"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA } , { L"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT } ,
{ L"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT } , { L"HKEY_USERS", HKEY_USERS } };
// Read kHKeys
CodePudding user response:
It is not a must to construct kHKeys
before the threads start to use reentrantFunction
.
As you can see here: static local variables, since C 11 is it guaranteed by the standard that a static local variable will be initialized only once. There is a specific note regarding locks that can be applied to ensure single initialazion in a multi threaded environment:
If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once).
Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison.
However - if you use a static variable that requires a relatively long initialization (not the case in your example), and your threads are required to perform according to some realtime requirements (with minimum delay), you can consider to do it in separate initialization phase, before the threads start to run.
CodePudding user response:
As requested a separate answer about constructing STL containers at compile time:
Note that some STL containers are now constexpr and the compiler might construct the value at compile time and place it in the .data section. You can (try to) force that by declaring it constinit.
As an example lets create an array of the first 5 primes:
#include <string>
#include <array>
constexpr std::array<int, 5> make_primes() {
std::array<int, 5> v;
for (int i = 0, j = 2; i < 5; j) {
for(int k = 2; k < j; k) {
if (j % k == 0) {
continue;
}
}
v[i ] = j;
}
return v;
}
int bla(int i)
{
static constinit std::array<int, 5> primes = make_primes();
return primes[i];
}
This results in the following code: https://godbolt.org/z/a5h6TE9f7
bla(int):
movsx rdi, edi
mov eax, DWORD PTR bla(int)::primes[0 rdi*4]
ret
bla(int)::primes:
.long 2
.long 3
.long 4
.long 5
.long 6
As you can see nowhere in the compiled code are any primes calculated. That happened at compile time and the resulting std::array
is placed in the data section (section not visible).
Because I used constinit
the compiler will fail if the variable can not be computed at compile time. If I had used constexpr
the compiler would try to compute the value a compile time but might decide the computation is to expensive at compile time and initialize the variable at run time.