1+ // This file is part of CoreLibrary containing useful reusable utility
2+ // classes.
3+ //
4+ // Copyright (C) 2014 to present, Duncan Crutchley
5+ // Contact <15799155+dac1976@users.noreply.github.com>
6+ //
7+ // This program is free software: you can redistribute it and/or modify
8+ // it under the terms of the GNU Lesser General Public License as published
9+ // by the Free Software Foundation, either version 3 of the License, or
10+ // (at your option) any later version.
11+ //
12+ // This program is distributed in the hope that it will be useful,
13+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ // GNU General Public License and GNU Lesser General Public License
16+ // for more details.
17+ //
18+ // You should have received a copy of the GNU General Public License
19+ // and GNU Lesser General Public License along with this program. If
20+ // not, see <http://www.gnu.org/licenses/>.
21+
22+ #ifndef MANAGED_SINGLETON_HPP
23+ #define MANAGED_SINGLETON_HPP
24+
25+ #include < atomic>
26+ #include < memory>
27+ #include < mutex>
28+ #include < utility>
29+ #include < type_traits>
30+ #include " Platform/PlatformDefines.h"
31+
32+ // TODO: This needs testing and maybe tweaking, it is a work-in-progress as a
33+ // hopeful replacement for the Loki singleton.
34+
35+ namespace core_lib
36+ {
37+
38+ #if defined(IS_CPP14)
39+ // NOTE: T must be default-constructible for fallback instance.
40+ template <typename T>
41+ class ManagedSingleton_Modern
42+ {
43+ static_assert (std::is_default_constructible<T>::value,
44+ " ManagedSingleton requires T to be default constructible" );
45+
46+ public:
47+ template <typename ... Args>
48+ static T& Instance (Args&&... args)
49+ {
50+ if (m_destroyed.load (std::memory_order_relaxed))
51+ {
52+ return DestroyedFallback ();
53+ }
54+
55+ T* ptr = m_instance.load (std::memory_order_acquire);
56+
57+ if (nullptr == ptr)
58+ {
59+ std::lock_guard<std::mutex> lock (m_mutex);
60+
61+ ptr = m_instance.load (std::memory_order_relaxed);
62+
63+ if (nullptr == ptr)
64+ {
65+ if (m_destroyed.load (std::memory_order_relaxed))
66+ {
67+ return destroyed_fallback ();
68+ }
69+
70+ std::unique_ptr<T> newInstance =
71+ std::make_unique<T>(std::forward<Args>(args)...);
72+
73+ ptr = newInstance.get ();
74+
75+ m_instance.store (ptr, std::memory_order_release);
76+ m_owned = std::move (newInstance);
77+ }
78+ }
79+
80+ return *ptr;
81+ }
82+
83+ static T* TryInstance ()
84+ {
85+ return m_instance.load (std::memory_order_acquire);
86+ }
87+
88+ // Destroy() must only be called when no other threads
89+ // are accessing Instance() or TryInstance().
90+ static void Destroy ()
91+ {
92+ std::lock_guard<std::mutex> lock (m_mutex);
93+
94+ m_destroyed.store (true , std::memory_order_relaxed);
95+ m_owned.reset ();
96+ m_instance.store (nullptr , std::memory_order_release);
97+ }
98+
99+ private:
100+ static T& DestroyedFallback ()
101+ {
102+ static T fallbackInstance{};
103+ return fallbackInstance;
104+ }
105+
106+ private:
107+ static std::atomic<T*> m_instance;
108+ static std::unique_ptr<T> m_owned;
109+ static std::mutex m_mutex;
110+ static std::atomic<bool > m_destroyed;
111+ };
112+
113+ template <typename T>
114+ std::atomic<T*> ManagedSingleton_Modern<T>::m_instance{nullptr };
115+
116+ template <typename T>
117+ std::unique_ptr<T> ManagedSingleton_Modern<T>::m_owned;
118+
119+ template <typename T>
120+ std::mutex ManagedSingleton_Modern<T>::m_mutex;
121+
122+ template <typename T>
123+ std::atomic<bool > ManagedSingleton_Modern<T>::m_destroyed{false };
124+
125+ template <typename T>
126+ using ManagedSingleton = ManagedSingleton_Modern<T>;
127+ #else
128+ // NOTE: T must be default-constructible for fallback instance.
129+ template <typename T>
130+ class ManagedSingleton_Legacy
131+ {
132+ static_assert (std::is_default_constructible<T>::value,
133+ " ManagedSingleton requires T to be default constructible" );
134+
135+ public:
136+ template <typename ... Args>
137+ static T& Instance (Args&&... args)
138+ {
139+ if (m_destroyed.load (std::memory_order_relaxed))
140+ {
141+ return DestroyedFallback ();
142+ }
143+
144+ std::lock_guard<std::mutex> lock (m_mutex);
145+
146+ if (nullptr == m_instance)
147+ {
148+ if (m_destroyed.load (std::memory_order_relaxed))
149+ {
150+ return DestroyedFallback ();
151+ }
152+
153+ m_instance.reset (new T (std::forward<Args>(args)...));
154+ }
155+
156+ return *m_instance;
157+ }
158+
159+ static T* TryInstance ()
160+ {
161+ std::lock_guard<std::mutex> lock (m_mutex);
162+ return m_instance.get ();
163+ }
164+
165+ // Destroy() must only be called when no other threads are
166+ // accessing Instance() or TryInstance().
167+ static void Destroy ()
168+ {
169+ std::lock_guard<std::mutex> lock (m_mutex);
170+
171+ m_destroyed.store (true , std::memory_order_relaxed);
172+ m_instance.reset ();
173+ }
174+
175+ private:
176+ static T& DestroyedFallback ()
177+ {
178+ static T fallbackInstance{};
179+ return fallbackInstance;
180+ }
181+
182+ private:
183+ static std::unique_ptr<T> m_instance;
184+ static std::mutex m_mutex;
185+ static std::atomic<bool > m_destroyed;
186+ };
187+
188+ template <typename T>
189+ std::unique_ptr<T> ManagedSingleton_Legacy<T>::m_instance{nullptr };
190+
191+ template <typename T>
192+ std::mutex ManagedSingleton_Legacy<T>::m_mutex;
193+
194+ template <typename T>
195+ std::atomic<bool > ManagedSingleton_Legacy<T>::m_destroyed{false };
196+
197+ template <typename T>
198+ using ManagedSingleton = ManagedSingleton_Legacy<T>;
199+ #endif
200+
201+ } // namespace core_lib
202+
203+ #endif // MANAGED_SINGLETON_HPP
0 commit comments