iam
iam

Reputation: 1713

C++ multithreaded static POD initialisation within a function

I'm unsure if static POD's may actually be zero initialised multiple times if they are static variables within functions that are called by multiple threads:

void CalledByManyThreads()
{
    struct StaticClass
    {
        struct POD { volatile LONG value; } pod; // Using MSVC specific volatile behaviour - I don't care about std::atomic


        StaticClass()   //  Constructor makes the struct a non-POD so we likely need the extra POD struct for our member data
        {
            while (pod.value == 0)
                if (InterlockedCompareExchange(&pod.value, 0, 1) == 0)
                {
                    //  Is it possible that another thread zero initialises pod after we have just set it to 1?
                    break;
                }
        }
    };


    static StaticClass test;    //  When is test.pod zero initialised in MSVC 2013+?
}

Around this area of functionality there are differences between mandated C++ standards and the actual behaviour of compilers - and I am more interested in the reality of existing compilers from MSVC2013 onwards.

Is it possible that one thread sets pod.value to 1 but that after another competing thread zero initialises it back to 0?

Upvotes: 1

Views: 68

Answers (1)

mksteve
mksteve

Reputation: 13073

MSVC 2013 does not support "magic statics" MSDN : C++11 support. I have also verified at an earlier time the code generated, and confirmed it does not support them.

VS2015 does appear to work correctly (and marked as such).

The code generated in VS2013 is the same as earlier code, which has a thread-unsafe mechanism for detecting if the variable has been constructed. This is necessary. This test + the time constructing an object is the length of time the race occurs for.

After one item has been completely constructed, there are no further re-constructions.

There are a number of compiler issues I have found with the VS2013 (e.g. incorrect detection of AVX commands in the runtime library, when running in a VM), and Microsoft have recommended for any identified issues to upgrade the compiler. Microsoft connect : AVX generates illegal instruction

That would be my recommendation to you.

Is it possible that one thread sets pod.value to 1 but that after another competing thread zero initialises it back to 0?

The compiler has 3 plans for initializing static data within a program.

  • Very static data
  • Static-ish data
  • non pod data.

Very Static Data

When the compiler sees a data structure which can be completely known at compile time, then it will create memory for the structure within the obj file, which will have the correct layout for the final file. This will not get initialized multiple times.

Static-ish data

If the POD structure is being initialized with information which is not completely known at compile time, then a constructor for the unknown element will be created. In the case I have identified

struct memoryAllocator {
     void * (*mallocFunction)( size_t size );
     int initialized;
}

memoryAllocator alloc { malloc, 1 };

alloc was initialized with initialized set to 1 as static data, and then constructed before main with the value of the imported malloc. There seems to be no C++ requirement that construction of an object is all-or-nothing. Which seems an oversight to me.

non pod data

This is initialized as a constructor. When in a static function, the code is guarded by a variable which describes if initialization has completed.

From looking at your code, you have

  1. Not initialized the data in the POD structure
  2. Not written code where the value is able to transition from 1 to 0.

In this term, I would expect the POD data to be left un-initialized and 0 (as it is static), and the constructor is the only item to be called, and will modify data once to 1 (due to the interlocked).

Upvotes: 1

Related Questions