Singleton Implementation 7/1/2016 CS 631: Singleton Implementation 1 Outline • Dead Reference Problem – Dead Reference Detection • • • • The Phoenix Singleton Policy Classes Template Template Parameters Assembling SingletonHolder 7/1/2016 CS 631: Singleton Implementation 2 Dead Reference Problem • Example – Another singleton Logger reports any events in the system. – When it shuts down it needs to report the latest state of the market. • Problem – Assume both Market and Logger are Meyers singletons. – The runtime C++ support destroys local static objects in the reverse order of their creation. – If the Market is created before the Logger it will be destroyed first. – The Logger needs to report the latest market at the destruction, but it will get a reference to the "shell" of the destroyed Market object. Undefined behavior!!! 7/1/2016 CS 631: Singleton Implementation 3 Detection of the Dead Reference class DeadRefMarket { public: static DeadRefMarket& getInstance() { if (!instance) if (flagDestroyed) onDeadReference(); else create(); return *instance; } virtual ~DeadRefMarket() { instance = 0; flagDestroyed = true; } ... 7/1/2016 CS 631: Singleton Implementation 4 Detection of the Dead Reference (cont.) private: DeadRefMarket() {} ... static void create() { static DeadRefCfg localInstance; instance = &localInstance; } static void onDeadReference() {throw std::runtime_error ("Dead reference detected");} static DeadRefMarket* instance; static bool flagDestroyed; }; DeadRefMarket* DeadRefMarket::instance = 0; bool DeadRefMarket::flagDestroyed = false; 7/1/2016 CS 631: Singleton Implementation 5 The Phoenix Singleton • The above implementation detects the dead reference, but does not ensure that the singleton is available at all times. • The Phoenix singleton ensures that an instance is created again if the dead reference is detected. 7/1/2016 CS 631: Singleton Implementation 6 The Phoenix Singleton: Implementation class PhoenixMarket { public: static PhoenixMarket& getInstance() { if (!instance) if (flagDestroyed) onDeadReference(); else create(); return *instance; } virtual ~PhoenixMarket() { instance = 0; flagDestroyed = true; } double getRate(double maturity) {return rates[maturity);} 7/1/2016 CS 631: Singleton Implementation 7 The Phoenix Singleton Implementation private: PhoenixMarket() {} static void create() { static PhoenixMarket localInstance; instance = &localInstance; } static void onDeadReference() { create(); // Create a new singleton at the instance // address. (C++ guarantees that the static object's // memory lasts for the duration of the program!) new(instance) PhoenixMarket; atexit(kill); flagDestroyed = false; } 7/1/2016 CS 631: Singleton Implementation 8 The Phoenix Singleton Implementation (cont.) void kill(void) { instance->~ PhoenixMarket(); } static PhoenixMarket* instance; static bool flagDestroyed; }; PhoenixMarket* PhoenixMarket ::instance = 0; bool PhoenixMarket::flagDestroyed = false; 7/1/2016 CS 631: Singleton Implementation 9 Policies • Policies help in implementing safe, efficient and highly customizable design elements. • A policy defines a class interface or a class template interface. • The interface consists of one or all of the following: – inner type definitions; – member functions; – member data. • Example: define policy classes that implement the Creator policy. 7/1/2016 CS 631: Singleton Implementation 10 Creator Policy: Using new() template <class T> struct OpNewCreator { static T* create() { return new T; } }; 7/1/2016 CS 631: Singleton Implementation 11 Creator Policy: Using malloc() template <class T> struct MallocCreator { static T* create() { void* buf = malloc(sizeof(T)); if (!buf) return 0; // placement new operator! return new(buf) T; } } 7/1/2016 CS 631: Singleton Implementation 12 Policy: Analysis • For a given policy there can be an unlimited number of implementations, which are called policy classes. • Unlike classic interfaces (abstract classes), policy interfaces are loosely defined. • Policies are syntax oriented, not signature oriented. – For example, the Creator policy does not specify that create must be static or virtual – the only requirement is that the class define a create member function. • Example – The Market class needs to create a new Index that holds specific rates. 7/1/2016 CS 631: Singleton Implementation 13 Creator Policy Usage // Library code: template <class CreationPolicy> class Market : public class CreationPolicy { ... Index* index = create(); ... }; // Client code: typedef Market<OpNewCreator<Index> > MyMarket; 7/1/2016 CS 631: Singleton Implementation 14 Template Template Parameters template <template<class Created> class CreationPolicy> class Market : public CreationPolicy<Index> { ... Index* index = create(); ... }; // The Created symbol can simply be omitted as it // does not contribute to the definition of Market: 7/1/2016 CS 631: Singleton Implementation 15 Template Template Parameters (cont.) // Library code: template <template<class> class CreationPolicy> class Market : public CreationPolicy<Index> { ... Index* index = create(); ... }; // Client code: typedef Market<OpNewCreator> MyMarket; 7/1/2016 CS 631: Singleton Implementation 16 Assembling SingletonHolder template < class T, template <class> class CreationPolicy = CreateUsingNew, template <class> class LifetimePolicy = DefaultLifetime > class SingletonHolder { public: 7/1/2016 CS 631: Singleton Implementation 17 Assembling SingletonHolder (cont.) static T& getInstance() { if (! instance) { if (flagDestroyed) { LifetimePolicy<T>::onDeadReference(); flagDestroyed = false; } instance = CreationPolicy<T>::create(); LifetimePolicy<T>::scheduleCall (&destroySingleton); } return *instance; } 7/1/2016 CS 631: Singleton Implementation 18 Assembling SingletonHolder (cont.) private: SingletonHolder(); static void destroySingleton() { CreationPolicy<T>::destroy(instance); instance = 0; flagDestroyed = false; } static T* instance; static bool flagDestroyed; }; template<class T> T* SingletonHolder<T>::instance = 0; template<class T> bool SingletonHolder<T>:: flagDestroyed = false; 7/1/2016 CS 631: Singleton Implementation 19 SingletonHolder: Creation Policy // Creation policy example: template <class T> struct CreateUsingNew { static T* create() { return new T; } static void destroy(T* p) { delete p; } }; 7/1/2016 CS 631: Singleton Implementation 20 SingletonHolder: Creation Policy // LifetimePolicy example: template <class T> struct DefaultLifetime { static void ScheduleCall(void (*pFun)()) {std::atexit(pFun);} static void onDeadReference() { throw std::logic_error ("Dead Reference Detected"); } }; // Client code: typedef SingletonHolder<Market> BasicMarket; 7/1/2016 CS 631: Singleton Implementation 21