Skip to content
Snippets Groups Projects
Commit 904e41e5 authored by Martyn Gigg's avatar Martyn Gigg
Browse files

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
parent 85136f89
No related branches found
No related tags found
No related merge requests found
...@@ -26,16 +26,17 @@ ...@@ -26,16 +26,17 @@
#include <cassert> #include <cassert>
#include <cstdlib> #include <cstdlib>
#include <functional> #include <functional>
#include <mutex>
namespace Mantid { namespace Mantid {
namespace Kernel { namespace Kernel {
/// Type of deleter function /// Type of deleter function
using deleter_t = std::function<void()>; using SingletonDeleterFn = std::function<void()>;
/// Register the given deleter function to be called /// Register the given deleter function to be called
/// at exit /// 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 /// Manage the lifetime of a class intended to be a singleton
template <typename T> class SingletonHolder { template <typename T> class SingletonHolder {
...@@ -45,8 +46,22 @@ public: ...@@ -45,8 +46,22 @@ public:
SingletonHolder() = delete; SingletonHolder() = delete;
static T &Instance(); 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 /// Policy class controlling creation of the singleton
/// Implementation classes should mark their default /// Implementation classes should mark their default
/// constructors private and insert a friend declaration /// constructors private and insert a friend declaration
...@@ -67,25 +82,18 @@ template <typename T> struct CreateUsingNew { ...@@ -67,25 +82,18 @@ template <typename T> struct CreateUsingNew {
/// already exist /// already exist
/// Creation is done using the CreateUsingNew policy at the moment /// Creation is done using the CreateUsingNew policy at the moment
template <typename T> inline T &SingletonHolder<T>::Instance() { template <typename T> inline T &SingletonHolder<T>::Instance() {
// Local static instance initialized by an immediately invoked lambda. std::call_once(once, []() {
// A second nested lambda captures the singleton instance pointer instance = CreateUsingNew<T>::create();
// and forms the deleter function that is registered with deleteOnExit(SingletonDeleterFn([]() {
// the atexit deleters.
#ifndef NDEBUG
static bool destroyed(false);
#endif
static T *instance = [] {
auto *singleton = CreateUsingNew<T>::create();
deleteOnExit(deleter_t([singleton]() {
#ifndef NDEBUG #ifndef NDEBUG
destroyed = true; destroyed = true;
#endif #endif
CreateUsingNew<T>::destroy(singleton); CreateUsingNew<T>::destroy(instance);
})); }));
return singleton; });
}(); #ifndef NDEBUG
assert(!destroyed); assert(!destroyed);
#endif
return *instance; return *instance;
} }
......
...@@ -6,29 +6,36 @@ namespace Kernel { ...@@ -6,29 +6,36 @@ namespace Kernel {
namespace { namespace {
/// List of functions to call on program exit /// List of functions to call on program exit
std::list<deleter_t> DELETERS; using CleanupList = std::list<SingletonDeleterFn>;
} // namespace
CleanupList &cleanupList() {
static CleanupList cleanup;
return cleanup;
}
/// Intended to be registered to atexit() that will clean up /// Intended to be registered to atexit() that will clean up
/// all registered singletons /// all registered singletons
/// This function may be registed with atexit() more than once, so it needs to /// This function may be registed with atexit() more than once, so it needs to
/// clear the list once it has called all the functions /// clear the list once it has called all the functions
MANTID_KERNEL_DLL void cleanupSingletons() { void cleanupSingletons() {
for (auto &deleter : DELETERS) { auto &deleters = cleanupList();
for (auto &deleter : deleters) {
deleter(); deleter();
} }
DELETERS.clear(); deleters.clear();
} }
} // namespace
/// Adds singleton cleanup function to our atexit list /// Adds singleton cleanup function to our atexit list
/// functions are added to the start of the list so on deletion it is last in, /// functions are added to the start of the list so on deletion it is last in,
/// first out /// first out
/// @param func :: Exit function to call - the singleton destructor function /// @param func :: Exit function to call - the singleton destructor function
MANTID_KERNEL_DLL void deleteOnExit(deleter_t func) { void deleteOnExit(SingletonDeleterFn func) {
if (DELETERS.empty()) { auto &deleters = cleanupList();
if (deleters.empty()) {
atexit(&cleanupSingletons); atexit(&cleanupSingletons);
} }
DELETERS.push_front(func); deleters.push_front(func);
} }
} // namespace Kernel } // namespace Kernel
} // namespace Mantid } // namespace Mantid
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment