From 904e41e5811c2182faf517abea53c7f987274037 Mon Sep 17 00:00:00 2001 From: Martyn Gigg <martyn.gigg@gmail.com> Date: Mon, 30 Jul 2018 08:53:27 +0100 Subject: [PATCH] Revert (but cleanup) SingletonHolder implementation MSVC 2017 15.7 contains a bug where constant initialization no longer takes place for objects with constructors marked constexpr such as once_flag. This leaves once_flag susceptible to dynamic initialization order problems and causes some singleton instances to be constructed incorrectly. We put back our correct implementation (with some cleanup) and await a fix from MS. Refs #0 --- .../Kernel/inc/MantidKernel/SingletonHolder.h | 40 +++++++++++-------- Framework/Kernel/src/SingletonHolder.cpp | 23 +++++++---- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Framework/Kernel/inc/MantidKernel/SingletonHolder.h b/Framework/Kernel/inc/MantidKernel/SingletonHolder.h index bb5d1ecbeaa..6321775214b 100644 --- a/Framework/Kernel/inc/MantidKernel/SingletonHolder.h +++ b/Framework/Kernel/inc/MantidKernel/SingletonHolder.h @@ -26,16 +26,17 @@ #include <cassert> #include <cstdlib> #include <functional> +#include <mutex> namespace Mantid { namespace Kernel { /// Type of deleter function -using deleter_t = std::function<void()>; +using SingletonDeleterFn = std::function<void()>; /// Register the given deleter function to be called /// at exit -extern MANTID_KERNEL_DLL void deleteOnExit(deleter_t); +MANTID_KERNEL_DLL void deleteOnExit(SingletonDeleterFn func); /// Manage the lifetime of a class intended to be a singleton template <typename T> class SingletonHolder { @@ -45,8 +46,22 @@ public: SingletonHolder() = delete; static T &Instance(); + +private: + static T *instance; + static std::once_flag once; +#ifndef NDEBUG + static bool destroyed; +#endif }; +// Static field initializers +template <typename T> T *SingletonHolder<T>::instance = nullptr; +template <typename T> std::once_flag SingletonHolder<T>::once; +#ifndef NDEBUG +template <typename T> bool SingletonHolder<T>::destroyed = false; +#endif + /// Policy class controlling creation of the singleton /// Implementation classes should mark their default /// constructors private and insert a friend declaration @@ -67,25 +82,18 @@ template <typename T> struct CreateUsingNew { /// already exist /// Creation is done using the CreateUsingNew policy at the moment template <typename T> inline T &SingletonHolder<T>::Instance() { -// Local static instance initialized by an immediately invoked lambda. -// A second nested lambda captures the singleton instance pointer -// and forms the deleter function that is registered with -// the atexit deleters. - -#ifndef NDEBUG - static bool destroyed(false); -#endif - static T *instance = [] { - auto *singleton = CreateUsingNew<T>::create(); - deleteOnExit(deleter_t([singleton]() { + std::call_once(once, []() { + instance = CreateUsingNew<T>::create(); + deleteOnExit(SingletonDeleterFn([]() { #ifndef NDEBUG destroyed = true; #endif - CreateUsingNew<T>::destroy(singleton); + CreateUsingNew<T>::destroy(instance); })); - return singleton; - }(); + }); +#ifndef NDEBUG assert(!destroyed); +#endif return *instance; } diff --git a/Framework/Kernel/src/SingletonHolder.cpp b/Framework/Kernel/src/SingletonHolder.cpp index 66e6b3b7888..c46dd2d5020 100644 --- a/Framework/Kernel/src/SingletonHolder.cpp +++ b/Framework/Kernel/src/SingletonHolder.cpp @@ -6,29 +6,36 @@ namespace Kernel { namespace { /// List of functions to call on program exit -std::list<deleter_t> DELETERS; -} // namespace +using CleanupList = std::list<SingletonDeleterFn>; + +CleanupList &cleanupList() { + static CleanupList cleanup; + return cleanup; +} /// Intended to be registered to atexit() that will clean up /// all registered singletons /// This function may be registed with atexit() more than once, so it needs to /// clear the list once it has called all the functions -MANTID_KERNEL_DLL void cleanupSingletons() { - for (auto &deleter : DELETERS) { +void cleanupSingletons() { + auto &deleters = cleanupList(); + for (auto &deleter : deleters) { deleter(); } - DELETERS.clear(); + deleters.clear(); } +} // namespace /// Adds singleton cleanup function to our atexit list /// functions are added to the start of the list so on deletion it is last in, /// first out /// @param func :: Exit function to call - the singleton destructor function -MANTID_KERNEL_DLL void deleteOnExit(deleter_t func) { - if (DELETERS.empty()) { +void deleteOnExit(SingletonDeleterFn func) { + auto &deleters = cleanupList(); + if (deleters.empty()) { atexit(&cleanupSingletons); } - DELETERS.push_front(func); + deleters.push_front(func); } } // namespace Kernel } // namespace Mantid -- GitLab