Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente |
helloworld:design_pattern:singleton:cplusplus [2017/06/29 22:49] – Ajout de la 2ème partie de Double-Checked root | helloworld:design_pattern:singleton:cplusplus [2022/07/04 10:06] (Version actuelle) – [Solution] : lock_guard->scoped_lock root |
---|
| ====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}} |
| |
<blockquote> | <blockquote> |
Consider again pInstance = new Singleton;, the line that initializes pInstance. This statement causes three things to happen: | Consider again pInstance = new Singleton;, the line that initializes pInstance. This statement causes three things to happen: |
* Step 3. Make pInstance point to 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. | 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. |
<cite>[[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.htm.maff |Archive}})</cite> | <cite>[[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}}</cite> |
<cite>[[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.htm.maff |Archive}})</cite> | <cite>[[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}}</cite> |
</blockquote> | </blockquote> |
| |
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. | 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/|Source]], {{ :helloworld:design_pattern:singleton:cplusplus:double-checked_locking_is_fixed_in_c_11.htm.maff |Archive}}) : | ====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 : |
<code cpp> | <code cpp> |
std::atomic<Singleton*> Singleton::m_instance; | std::atomic<Singleton*> Singleton::m_instance; |
| |
Singleton* Singleton::getInstance() { | Singleton* Singleton::getInstance() { |
Singleton* tmp = m_instance.load(std::memory_order_relaxed); | Singleton* tmp = m_instance.load(std::memory_order_acquire); |
std::atomic_thread_fence(std::memory_order_acquire); | |
if (tmp == nullptr) { | if (tmp == nullptr) { |
std::lock_guard<std::mutex> lock(m_mutex); | std::scoped_lock<std::mutex> lock(m_mutex); |
tmp = m_instance.load(std::memory_order_relaxed); | tmp = m_instance.load(std::memory_order_relaxed); |
if (tmp == nullptr) { | if (tmp == nullptr) { |
tmp = new Singleton; | tmp = new Singleton; |
std::atomic_thread_fence(std::memory_order_release); | m_instance.store(tmp, std::memory_order_release); |
m_instance.store(tmp, std::memory_order_relaxed); | |
} | } |
} | } |
return tmp; | return tmp; |
} | } |
| </code> |
| |
| Si constructeur non trivial : |
| <code cpp> |
| 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; |
| } |
| }; |
</code> | </code> |