Double-Checked Locking Pattern

This pattern now are widely criticized because of some compiler optimizations.

To create an singleton when the first time it is accessed, a solution is:

1
2
3
4
5
6
7
8
Singleton *Singleton::pInstance = 0;
Singleton& Singleton::Instance() {
    Lock guard(mutex);
    if (!pInstance) {
        pInstance = new Singleton;
    }
    return *pInstance;
}

But the problem is we have to lock the mutex each time we access the singleton. Actually only the first accessing should be locked. But the following solution is incorrect:

1
2
3
4
5
6
7
8
Singleton *Singleton::pInstance = 0;
Singleton& Singleton::Instance() {
    if (!pInstance) {
        Lock guard(mutex);
        pInstance = new Singleton;
    }
    return *pInstance;
}

The race condition appears again.

Double-Checked Locking Pattern:

1
2
3
4
5
6
7
8
9
10
Singleton *Singleton::pInstance = 0;
Singleton& Singleton::Instance() {
    if (!pInstance) {
    Lock guard(mutex);
        if (!pInstance) {
            pInstance = new Singleton;
        }
    }
    return *pInstance;
}

But this technique may not compromise with compiler’s optimization, it may cause run time error. For compiler, it will not care about the potential concurrency of the program, it just generate correct code for single thread. The value of “pInstance” seems not changed between the two if(!pInstance) statement, so the compiler will may remove the inner if(!pInstance).

Maybe we can consider the following modified DCL technique, provide a separate initialization function, user can initialize the singleton before using it, or he can use instance directly:

1
2
3
4
5
6
7
8
9
10
11
12
13
Singleton *Singleton::pInstance = 0;
void Singleton::Initialize() {
    Lock guard(mutex);
    if (!pInstance) {
        pInstance = new Singleton;
    }
}
Singleton& Singleton::Instance() {
    if (!pInstance) {
        Initialize();
    }
    return *pInstance;
}

Now, the first and the second phase of the DCL are in the different function, the compiler can’t perform the optimization which removes the inner if(!pInstance) statement.