Pregunta

Before C++11 local static variables were not thread safe. I need to initialize a static variable in a performance critical function with a result of some non-reentrant function.

I would like to see a static variable initialization which uses mutexes or other synchronization primitives, but has some opportunistic check of a regular static variable to reduce the mutex use in a typical situation when the variable has already been initialized a long ago. It seems GCC implements something like this for C++11, as mentioned in Static initialization and thread safety, but the code listed there is not complete and is assembly only.

Note: There is plenty of questions asking IF the static variable initialization is atomic on Stack Overflow, but they seem to be content with the answer "no" and they do not seems to show a practical solution (like C++ Static Initializer - Is it thread safe).

¿Fue útil?

Solución

I discussed this in a follow-up to the blog post referenced in the question. If for some reason you can't use boost::call_once your block-scoped static is a pointer, POD, or has a thread-safe constructor, you can write the same initialization guard code that GCC would emit:

// Define a static local variable once, safely, for MSVC
//
// This macro is necessary because MSVC pre-2013 doesn't
// properly implement C++11 static local initialization.
// It is equivalent to writing something like
//
//     static type var = stmt;
//
// in a compliant compiler (e.g. GCC since who knows when)

// States for lock checking
enum { uninitialized = 0, initializing, initialized };

// Preprocessor hackery for anonymous variables
#define PASTE_IMPL(x, y) x ## y
#define PASTE(x, y) PASTE_IMPL(x, y)
#define ANON_VAR(var) PASTE(var, __LINE__)

#define STATIC_DEFINE_ONCE(type, var, stmt)                     \
    static type var;                                            \
    static int ANON_VAR(state);                                 \
    bool ANON_VAR(cont) = true;                                 \
    while (ANON_VAR(cont)) {                                    \
        switch (InterlockedCompareExchange(&ANON_VAR(state),    \
                initializing, uninitialized)) {                 \
        case uninitialized:                                     \
            var = stmt;                                         \
            InterlockedExchange(&ANON_VAR(state), initialized); \
            ANON_VAR(cont) = false;                             \
            break;                                              \
        case initializing:                                      \
            continue;                                           \
        case initialized:                                       \
            ANON_VAR(cont) = false;                             \
            break;                                              \
        }                                                       \
    } do { } while (0)

You can use this like

void concurrently_accessed() {
    STATIC_DEFINE_ONCE(int, local_var, thread_unsafe_initializer());
    // ...
}

This approach takes advantage of zero-initialization of static block-scoped variables, which is required by the C language standard. The above macros will let you safely use "magic" statics until actual compiler & run-time support arrive in MSVC 2014.

Otros consejos

You could put the static data in a function and utilize boost::once:

int& get_static() {
    static boost::once_flag once_flag = BOOST_ONCE_INIT;
    static int* data;

    struct Initialize
    {
        static void apply() {
            data = new int(1);
        }
    };
    boost::call_once(once_flag, &Initialize::apply);
    return *data;
}

The data will be static initialized at first function call, after that the call once is done.

In http://www.boost.org/doc/libs/1_32_0/doc/html/call_once.html:

The call_once function and once_flag type (statically initialized to BOOST_ONCE_INIT) can be used to run a routine exactly once. This can be used to initialize data in a thread-safe manner.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top