Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A valid singleton Class ?

class Singleton
{
 private:
     static Singleton s;
     Singleton(){}
 public:
    static Singleton *getInstance()
    {
        return &s;
    }
};

Singleton Singleton::s;

Is this a valid singleton class?

class Singleton
{
 private:
     static Singleton *m_instance;
     Singleton(){}
 public:
    static Singleton *getInstance()
    {
        return m_instance;
    }

};
Singleton * Singleton::m_instance = new Singleton;

.

class Singleton
{
 private:
     static Singleton *m_instance;
     Singleton(){}
 public:
    static Singleton *getInstance()
    {
        if(m_instance == NULL)
        {
            lock();
            if(m_instance == NULL)
                m_instance = new Singleton;
            unlock();
        }
        return m_instance;
    }

};
Singleton * Singleton::m_instance = NULL;

The three singleton classes above both are thread safe, but they are both prone to "static initialization order fiasco", am I right?

like image 687
tenos Avatar asked Jan 11 '23 21:01

tenos


2 Answers

Is this a valid singleton class?

Now, after the edit the answer is yes, it is valid & it is also thread safe since all non-function-scope static variables are constructed before main(), while there is only one active thread.

C++ Standard n3337 § 3.6.2/1 § 3.6.2/2: Initialization of non-local variables

There are two broad classes of named non-local variables: those with static storage duration (3.7.1) and those with thread storage duration (3.7.2). Non-local variables with static storage duration are initialized as a consequence of program initiation. Non-local variables with thread storage duration are initialized as a consequence of thread execution. Within each of these phases of initiation, initialization occurs as follows.

Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place. Constant initialization is performed:

— if each full-expression (including implicit conversions) that appears in the initializer of a reference with static or thread storage duration is a constant expression (5.19) and the reference is bound to an lvalue designating an object with static storage duration or to a temporary (see 12.2);

— if an object with static or thread storage duration is initialized by a constructor call, if the constructor is a constexpr constructor, if all constructor arguments are constant expressions (including conversions), and if, after function invocation substitution (7.1.5), every constructor call and full-expression in the mem-initializers and in the brace-or-equal-initializers for non-static data members is a constant expression;

— if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.

Together, zero-initialization and constant initialization are called static initialization; all other initial- ization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place. (...)

C++ Standard n3337 § 6.7/4: Declaration statement

The zero-initialization (8.5) of all block-scope variables with static storage duration (3.7.1) or thread storage duration (3.7.2) is performed before any other initialization takes place. Constant initialization (3.6.2) of a block-scope entity with static storage duration, if applicable, is performed before its block is first entered. An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope. Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization*). (...)

*):

The implementation must not introduce any deadlock around execution of the initializer.

But it is still prone to static initialization order fiasco. The common way to write getInstance is:

Singleton& getInstance()
{
    static Singleton instance;
    return instance;
}

This way you can avoid this initialization problem.

Is this a thread-safe singleton class?

In C++11 above code is thread safe. In C++03 you can use

pthread_once


Besides this you should also prevent from copying and assignment:

Singleton( Singleton const&);      // Don't Implement
void operator=( Singleton const&); // Don't implement
like image 127
4pie0 Avatar answered Jan 17 '23 17:01

4pie0


As far as I can tell, it's thread safe. But it's susceptible to static initialization order fiasco.

If an object tries to access Singleton in it's constructor and that object is constructed during program initialization and this code is in another compilation unit than Singleton, it may or may not crash because Singleton::s may or may not have been initialized yet (because the order of initialization of static objects across compilation units is undefined). Here is an example:

// in another compilation unit, far far away
struct Foo {
    Foo() {
        Singleton::getInstance();
    }
};
Foo foo;
like image 38
eerorika Avatar answered Jan 17 '23 17:01

eerorika