====Problème==== [[http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html|The "Double-Checked Locking is Broken" Declaration]] {{ :helloworld:design_pattern:singleton:cplusplus:the_double-checked_locking_is_broken_declaration_2020-04-28_8_38_20_am_.html |Archive le 28/04/2020}} [[http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf|C++ and the Perils of Double-Checked Locking]] {{ :helloworld:design_pattern:singleton:cplusplus:ddj_jul_aug_2004_revised.pdf |Archive}}
Consider again pInstance = new Singleton;, the line that initializes pInstance. This statement causes three things to happen: * Step 1. Allocate memory to hold a Singleton object. * Step 2. Construct a Singleton object in the allocated memory. * Step 3. Make pInstance point to the allocated memory. Of critical importance is the observation that compilers are not constrained to perform these steps in this order! **In particular, compilers are sometimes allowed to swap Steps 2 and 3**. Why they might want to do that is a question we'll address in a moment. For now, let's focus on what happens if they do. [[http://www.drdobbs.com/cpp/c-and-the-perils-of-double-checked-locki/184405726|C++ and The Perils of Double-Checked Locking: Part I]] {{ :helloworld:design_pattern:singleton:cplusplus:c_and_the_perils_of_double-checked_locking_part_i_dr_dobb_s_2020-04-28_8_41_39_am_.html |Archive du 01/07/2004 le 28/04/2020}} [[http://www.drdobbs.com/cpp/c-and-the-perils-of-double-checked-locki/184405772|C++ and The Perils of Double-Checked Locking: Part II]] {{ :helloworld:design_pattern:singleton:cplusplus:c_and_the_perils_of_double-checked_locking_part_ii_dr_dobb_s_2020-04-28_8_41_46_am_.html |Archive du 01/08/2004 le 28/04/2020}}
En gros, le pointeur peut être différent de ''null'' mais pour autant l'objet peut ne pas avoir été construit si le thread est arrêté entre l'étape 2 et 3 et qu'elles sont inversées. ====Solution==== [[http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/|Double-Checked Locking is Fixed In C++11]] {{ :helloworld:design_pattern:singleton:cplusplus:double-checked_locking_is_fixed_in_c_11_2020-04-28_8_39_00_am_.html |Archive du 30/09/2013 le 28/04/2020}} [[http://www.modernescpp.com/index.php/thread-safe-initialization-of-a-singleton|Thread-Safe Initialization of a Singleton]] {{ :helloworld:design_pattern:singleton:cplusplus:thread-safe_initialization_of_a_singleton_-_modernescpp.com_2020-04-28_8_40_06_am_.html |Archive du 30/08/2016 le 28/04/2020}} Si le singleton possède un constructeur trivial : std::atomic Singleton::m_instance; std::mutex Singleton::m_mutex; Singleton* Singleton::getInstance() { Singleton* tmp = m_instance.load(std::memory_order_acquire); if (tmp == nullptr) { std::scoped_lock lock(m_mutex); tmp = m_instance.load(std::memory_order_relaxed); if (tmp == nullptr) { tmp = new Singleton; m_instance.store(tmp, std::memory_order_release); } } return tmp; } Si constructeur non trivial : class MySingleton{ public: static MySingleton& getInstance() { std::call_once(initInstanceFlag, &MySingleton::initSingleton); return *instance; } private: static MySingleton* instance; static std::once_flag initInstanceFlag; static void initSingleton() { instance= new MySingleton; } };