[vlmc-devel] Rework our Singleton to provide multiple policy.

Hugo Beauzée-Luyssen git at videolan.org
Mon Jul 4 15:34:28 CEST 2016


vlmc | branch: medialibrary | Hugo Beauzée-Luyssen <hugo at beauzee.fr> | Wed Jun 22 13:24:14 2016 +0200| [8f0edccf71ba0b5db176f5dfff52b5effd391cdc] | committer: Hugo Beauzée-Luyssen

Rework our Singleton to provide multiple policy.

This is mostly aiming at allowing Core to be destroyed within the scope
of main()

> https://code.videolan.org/videolan/vlmc/commit/8f0edccf71ba0b5db176f5dfff52b5effd391cdc
---

 src/Backend/MLT/MLTBackend.h       |   2 +-
 src/Gui/LanguageHelper.h           |   2 +-
 src/Gui/widgets/NotificationZone.h |   2 +-
 src/Main/Core.cpp                  |   7 --
 src/Main/Core.h                    |   7 +-
 src/Main/main.cpp                  |   2 +
 src/Tools/Singleton.hpp            | 149 ++++++++++++++++++++++++++++++++-----
 7 files changed, 138 insertions(+), 33 deletions(-)

diff --git a/src/Backend/MLT/MLTBackend.h b/src/Backend/MLT/MLTBackend.h
index 4c2cc6d..a8022de 100644
--- a/src/Backend/MLT/MLTBackend.h
+++ b/src/Backend/MLT/MLTBackend.h
@@ -66,7 +66,7 @@ class MLTBackend : public IBackend, public Singleton<MLTBackend>
 
         std::map<std::string, IFilterInfo*>    m_availableFilters;
 
-    friend Singleton<MLTBackend>;
+    friend Singleton_t::AllowInstantiation;
 };
 
 } // MLT
diff --git a/src/Gui/LanguageHelper.h b/src/Gui/LanguageHelper.h
index 6298c98..c646282 100644
--- a/src/Gui/LanguageHelper.h
+++ b/src/Gui/LanguageHelper.h
@@ -43,7 +43,7 @@ private:
     QTranslator             *m_translator;
     QTranslator             *m_qtTranslator;
 
-    friend class    Singleton<LanguageHelper>;
+    friend Singleton_t::AllowInstantiation;
 };
 
 #endif // LANGUAGEHELPER_H
diff --git a/src/Gui/widgets/NotificationZone.h b/src/Gui/widgets/NotificationZone.h
index 824386e..a697730 100644
--- a/src/Gui/widgets/NotificationZone.h
+++ b/src/Gui/widgets/NotificationZone.h
@@ -63,7 +63,7 @@ class NotificationZone : public QWidget, public Singleton<NotificationZone>
     private slots:
         void        hideNotification();
 
-    friend class    Singleton<NotificationZone>;
+    friend Singleton_t::AllowInstantiation;
 };
 
 #endif // NOTIFICATIONZONE_H
diff --git a/src/Main/Core.cpp b/src/Main/Core.cpp
index 34d98f5..1dc3bcf 100644
--- a/src/Main/Core.cpp
+++ b/src/Main/Core.cpp
@@ -166,10 +166,3 @@ Core::runtime()
 {
     return m_timer.elapsed();
 }
-
-Core*
-Core::instance()
-{
-    static Core core;
-    return &core;
-}
diff --git a/src/Main/Core.h b/src/Main/Core.h
index ed10319..37c3838 100644
--- a/src/Main/Core.h
+++ b/src/Main/Core.h
@@ -44,8 +44,9 @@ namespace Commands
 }
 
 #include <QElapsedTimer>
+#include "Tools/Singleton.hpp"
 
-class Core
+class Core : public ScopedSingleton<Core>
 {
     public:
         Backend::IBackend*      backend();
@@ -65,8 +66,6 @@ class Core
         bool                    loadProject( const QString& fileName );
         bool                    newProject( const QString& projectName, const QString& projectPath );
 
-        static Core*            instance();
-
     private:
         Core();
         ~Core();
@@ -85,6 +84,8 @@ class Core
         Commands::AbstractUndoStack*   m_undoStack;
         Library*                m_library;
         QElapsedTimer           m_timer;
+
+        friend Singleton_t::AllowInstantiation;
 };
 
 #endif // CORE_H
diff --git a/src/Main/main.cpp b/src/Main/main.cpp
index 93c3f4b..dc03f9e 100644
--- a/src/Main/main.cpp
+++ b/src/Main/main.cpp
@@ -94,6 +94,7 @@ VLMCGuimain( int argc, char **argv )
 
     Backend::IBackend* backend;
     VLMCmainCommon( app, &backend );
+    auto coreLock = Core::Policy_t::lock();
 
     /* Load a project file */
     bool        project = false;
@@ -186,6 +187,7 @@ VLMCCoremain( int argc, char **argv )
 
     Backend::IBackend* backend;
     VLMCmainCommon( app, &backend );
+    auto coreLock = Core::Policy_t::lock();
 
     /* Load a project file */
     if ( app.arguments().count() < 3 )
diff --git a/src/Tools/Singleton.hpp b/src/Tools/Singleton.hpp
index 9ac7071..29266f5 100644
--- a/src/Tools/Singleton.hpp
+++ b/src/Tools/Singleton.hpp
@@ -31,40 +31,149 @@
 #ifndef SINGLETON_HPP
 #define SINGLETON_HPP
 
-#include <stdlib.h>
+#include <memory>
+#include <mutex>
 
+namespace details
+{
+namespace policy
+{
 template <typename T>
-class       Singleton
+struct OldPolicy
 {
-public:
-    static T*      instance()
+    static T* get()
     {
-        if ( m_instance == nullptr )
-            m_instance = new T;
-        return m_instance;
+        if (s_instance == nullptr)
+            s_instance = new T;
+        return s_instance;
     }
 
-    static void    destroyInstance()
+    static void release()
     {
-        if ( m_instance != nullptr )
+        delete s_instance;
+        s_instance = nullptr;
+    }
+
+    using InstanceType = T*;
+
+private:
+    static T* s_instance;
+};
+
+template <typename T>
+T* OldPolicy<T>::s_instance = nullptr;
+
+template <typename T>
+struct MeyersPolicy
+{
+    static T* get()
+    {
+        static T inst;
+        return &inst;
+    }
+    // No release method; automatically released when program exits
+};
+
+template <typename T>
+class ScopedPolicy
+{
+    struct Lock
+    {
+        std::shared_ptr<T> ptr;
+        friend class ScopedPolicy;
+    };
+
+public:
+    static Lock lock()
+    {
+        auto inst = s_weak_inst.lock();
+        if ( inst )
+            return Lock{ inst };
+        std::unique_lock<std::mutex> lock( s_mutex );
+        inst = s_weak_inst.lock();
+        if ( !inst )
         {
-            delete m_instance;
-            m_instance = nullptr;
+            // use a lambda expression since the shared_ptr doesn't have access to T::~T()
+            s_weak_inst = inst = std::shared_ptr<T>( new T, [](T*ptr){ delete ptr; } );
         }
+        return Lock{ inst };
+    }
+
+    static std::shared_ptr<T> get()
+    {
+        return lock().ptr;
+    }
+
+    static std::mutex s_mutex;
+    static std::weak_ptr<T> s_weak_inst;
+};
+
+template <typename T>
+std::mutex ScopedPolicy<T>::s_mutex;
+template <typename T>
+std::weak_ptr<T> ScopedPolicy<T>::s_weak_inst;
+}
+
+template <typename T, typename Policy>
+class       PSingleton
+{
+public:
+    using Singleton_t = PSingleton<T, Policy>;
+    /// Meant to be used by Singleton<> users to declare the Policy as a friend class.
+    /// The AllowInstantiation just reads better than Singleton<T>::Policy_t
+    using AllowInstantiation = Policy;
+    using Policy_t = Policy;
+
+    static auto      instance() -> decltype(Policy::get())
+    {
+        return Policy::get();
+    }
+
+    static void    destroyInstance()
+    {
+        Policy::release();
     }
-protected:
-    Singleton(){}
-    ~Singleton() = default;
-    Singleton(const Singleton<T>&) = delete;
-    Singleton(Singleton<T>&&) = delete;
-    Singleton<T>&   operator=(const Singleton<T>&) = delete;
-    Singleton<T>&   operator=(Singleton<T>&&) = delete;
 
 protected:
-    static T*      m_instance;
+    PSingleton(){}
+    ~PSingleton() = default;
+
+public:
+    PSingleton(const Singleton_t&) = delete;
+    PSingleton(Singleton_t&&) = delete;
+    Singleton_t&   operator=(const Singleton_t&) = delete;
+    Singleton_t&   operator=(Singleton_t&&) = delete;
 };
 
+}
+
+/**
+ * @brief Soingleton defines the "old" way of having a singleton.
+ * ie. by having to call instance()/destroyInstance() couple
+ * @warning This isn't thread safe
+ */
+template <typename T>
+using Singleton = details::PSingleton<T, details::policy::OldPolicy<T>>;
+
+/**
+ * @brief MeyersPolicy defines the usual C++11 singleton
+ * This is threadsafe, but doesn't let you control the moment the instance will be
+ * released
+ */
 template <typename T>
-T*  Singleton<T>::m_instance = nullptr;
+using MeyersSingleton = details::PSingleton<T, details::policy::MeyersPolicy<T>>;
+
+/**
+ * @brief Defined a singleton that will be released when the originally acquied "lock"
+ * falls out of scope
+ * Inherithing from this singleton implementation will provide a T::Policy_t::lock() method
+ * which needs to be called to acquire a lock.
+ * When this lock falls out of scope, any subsequent call to lock()/instance() will create a new instance.
+ * Calling instance() without having a lock instantiated will create a new instance that will get destroyed
+ * when the returned instance falls out of scope.
+ */
+template <typename T>
+using ScopedSingleton = details::PSingleton<T, details::policy::ScopedPolicy<T>>;
+
 
 #endif // SINGLETON_HPP



More information about the Vlmc-devel mailing list