0

So my concern is hitting a function which creates a thread before main() gets called. At that point, thread safe functions need to have a mutex which is ready. Only, there is no way to guarantee the order in which global objects get initialized in C++ (well, to some extend, we can, but still, in a large project, good luck with that!)

So I like to have my global objects, such as singletons, allocated at runtime. That way, they get initialized the first time you call their instance() function and that means they are ready when needed. However, if I have two or more threads running, they may each end up creating an instance unless I make sure to have a valid lock in that function.

I want to make sure that using the following initialization:

pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;

always happen at compile time and thus that global_mutex is always going to be ready for pthread_mutex_lock().

This means my singleton (and other classes that need initialization before main() gets called) can rely on that mutex as in the following simplified code:

// singleton.h
class singleton
{
public:
    static singleton * instance();

    ...other functions...

private:
    singleton();
};

// singleton.cpp
pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;

singleton *  global_singleton = nullptr;

singleton::singleton()
{
    // do some initialization
}

singleton * singleton::instance()
{
    pthread_mutex_lock(&global_mutex);  // <- works even before main() called?
    if(global_singleton == nullptr)
    {
        global_singleton = new singleton();
    }
    pthread_mutex_unlock(&global_mutex);
    return global_singleton;
}

Note: I'm not asking about the validity of the singleton pattern. Just about the mutex. Also, no, you can't safely use a smart pointer in the global variable because you can't know whether it will be initialized before you call the instance() function. If you want to return a smart pointer, you can always use a bare pointer to a smart pointer. It doesn't change much anything for the singleton. It still won't get destroyed automatically.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156

2 Answers2

1

In C++11, function-static variables are required by the standard to be initialized in a thread-safe fashion. How the implementation pulls that off is up to it, but the compiler is required to generate something to ensure thread safety.

So you can do this:

singleton * singleton::instance()
{
    static auto global_singleton = new singleton();
    return global_singleton;
}

Of course, you must understand that complex interactions of these "somethings" that make static initialization thread-safe may well lead to deadlocks. This is yet another reason why the singleton pattern should be avoided.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
1

One solution is to use the pthread_once mechanism which is designed to provide a guaranteed one-time initialisation for libraries:

Adapting your example, something like:

#include <pthread.h>
static pthread_once_t lib_is_initialized = PTHREAD_ONCE_INIT;
statec pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;

void initialise_lib()
{
   pthread_mutex_init(&global_mutex, NULL);
}

singleton * singleton::instance()
{
    pthread_once(&lib_is_initialized, initialize_lib);

    pthread_mutex_lock(&global_mutex);
    if(global_singleton == nullptr)
    {
        global_singleton = new singleton();
    }
    pthread_mutex_unlock(&global_mutex);
    return global_singleton;
}

However this is more complex than necessary. You can rely on the safe initialisation of a static variable being performed once, so I would recommend using the simpler solution posted by @Nicol Bolas.

gavinb
  • 19,278
  • 3
  • 45
  • 60
  • Actually, I like the idea, but I would initialize the singleton inside the `initialize_lib()` function. That would make more sense, I think. And I would not need the `global_mutex` (the `lib_is_initialized` object certainly has a mutex already.) – Alexis Wilke Sep 29 '19 at 02:33