Search code examples
c++singletondouble-checked-locking

What the correct way when use Double-Checked Locking with memory barrier in c++?


I just read the excellent blog C++ and the Perils of Double-Checked Locking

And I don't understand why we have to use the first memory barrier in Example 12 (as below):

Singleton* Singleton::instance () {
       Singleton* tmp = pInstance;
       ... // insert memory barrier
       if (tmp == 0) {
          Lock lock;
          tmp = pInstance;
          if (tmp == 0) {
             tmp = new Singleton;
             ... // insert memory barrier
             pInstance = tmp;
          }
       }
       return tmp;
    }

Is it safe to change it to code below? Why not?

Singleton* Singleton::instance () {
       if (pInstance == 0) {
          Lock lock;
          if (pInstance == 0) {
             Singleton* tmp = new Singleton;
             ... // insert memory barrier
             pInstance = tmp;
          }
       }
       return pInstance;
    }

Solution

  • No, it isn't safe. Reading the three paragraphs before the example, and the two after it, the potential problem is a system where the write to pInstance is done (flushed to memory) on thread B before the construction of Singleton has been flushed. Then thread A could read pInstance, see the pointer as non-null, and return it potentially allowing thread A to access the Singleton before thread B has finished storing it into memory.

    The first flush is necessary to ensure that the flushing of the writes during construction of Singleton have been completed before you try to use it in a different thread.

    Depending on the hardware you're running on this might not be a problem.